web-dev-qa-db-ja.com

Jacksonを使用してJSONを多態型にデシリアライズ-完全な例でコンパイルエラーが発生する

多態的なJSONの逆シリアル化を可能にするはずのプログラマーブルースのチュートリアルを実行しようとしています。

完全なリストはここにあります Programmer Bruce tutorials (素晴らしいものがあります)

最初の5つまで問題なく作業しましたが、最後の1つ(例6)にひっかかったのはもちろんです。

コンパイル時に次のエラーが表示されます

タイプObjectMapperのメソッドreadValue(JsonParser、Class)は、引数(ObjectNode、Class)には適用されません

そしてそれはコードの塊によって引き起こされています

  public Animal deserialize(  
      JsonParser jp, DeserializationContext ctxt)   
      throws IOException, JsonProcessingException  
  {  
    ObjectMapper mapper = (ObjectMapper) jp.getCodec();  
    ObjectNode root = (ObjectNode) mapper.readTree(jp);  
    Class<? extends Animal> animalClass = null;  
    Iterator<Entry<String, JsonNode>> elementsIterator =   
        root.getFields();  
    while (elementsIterator.hasNext())  
    {  
      Entry<String, JsonNode> element=elementsIterator.next();  
      String name = element.getKey();  
      if (registry.containsKey(name))  
      {  
        animalClass = registry.get(name);  
        break;  
      }  
    }  
    if (animalClass == null) return null;  
    return mapper.readValue(root, animalClass);
  }  
} 

具体的には

return mapper.readValue(root、animalClass);

以前に誰かがこれに遭遇しましたか?その場合、解決策はありましたか?

ジョンD.に感謝します。

60
Jon Driscoll

約束どおり、アノテーションを使用してポリモーフィックオブジェクトをシリアル化/逆シリアル化する方法の例を示しています。この例は、読んでいたチュートリアルのAnimalクラスに基づいています。

まず、AnimalクラスとサブクラスのJsonアノテーションを使用します。

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY)
@JsonSubTypes({
    @JsonSubTypes.Type(value = Dog.class, name = "Dog"),

    @JsonSubTypes.Type(value = Cat.class, name = "Cat") }
)
public abstract class Animal {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

次に、サブクラス、DogおよびCatを追加します。

public class Dog extends Animal {

    private String breed;

    public Dog() {

    }

    public Dog(String name, String breed) {
        setName(name);
        setBreed(breed);
    }

    public String getBreed() {
        return breed;
    }

    public void setBreed(String breed) {
        this.breed = breed;
    }
}

public class Cat extends Animal {

    public String getFavoriteToy() {
        return favoriteToy;
    }

    public Cat() {}

    public Cat(String name, String favoriteToy) {
        setName(name);
        setFavoriteToy(favoriteToy);
    }

    public void setFavoriteToy(String favoriteToy) {
        this.favoriteToy = favoriteToy;
    }

    private String favoriteToy;

}

ご覧のとおり、CatDogには特別なものはありません。それらについて知っているのはabstractクラスAnimalだけなので、逆シリアル化するときは、次のテストでわかるように、AnimalObjectMapperをターゲットにすると、実際のインスタンスが返されます。

public class Test {

    public static void main(String[] args) {

        ObjectMapper objectMapper = new ObjectMapper();

        Animal myDog = new Dog("ruffus","english shepherd");

        Animal myCat = new Cat("goya", "mice");

        try {
            String dogJson = objectMapper.writeValueAsString(myDog);

            System.out.println(dogJson);

            Animal deserializedDog = objectMapper.readValue(dogJson, Animal.class);

            System.out.println("Deserialized dogJson Class: " + deserializedDog.getClass().getSimpleName());

            String catJson = objectMapper.writeValueAsString(myCat);

            Animal deseriliazedCat = objectMapper.readValue(catJson, Animal.class);

            System.out.println("Deserialized catJson Class: " + deseriliazedCat.getClass().getSimpleName());



        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

Testクラスを実行した後の出力:

{"@type":"Dog","name":"ruffus","breed":"english shepherd"}

Deserialized dogJson Class: Dog

{"@type":"Cat","name":"goya","favoriteToy":"mice"}

Deserialized catJson Class: Cat

お役に立てれば、

ホセ・ルイス

106
jbarrueta

ジャクソンライブラリを介した多相性のシリアル化/逆シリアル化を有効にする簡単な方法は、ジャクソンオブジェクトマッパー(jackson.databind.ObjectMapper)をグローバルに構成して、抽象クラスなどの特定の種類のクラスの具体的なクラスタイプなどの情報を追加することです。

そのためには、マッパーが正しく構成されていることを確認してください。例えば:

オプション1:抽象クラス(およびオブジェクト型クラス)の多相シリアル化/逆シリアル化をサポート

jacksonObjectMapper.enableDefaultTyping(
    ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE); 

オプション2:抽象クラス(およびオブジェクト型クラス)、およびそれらの型の配列の多相シリアル化/逆シリアル化をサポートします。

jacksonObjectMapper.enableDefaultTyping(
    ObjectMapper.DefaultTyping.NON_CONCRETE_AND_ARRAYS); 

参照: https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization

2
AmitW

次にfasterxmlを使用する場合、

これらの変更が必要になる場合があります

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.node.ObjectNode;

メインメソッドで

つかいます

SimpleModule module =
  new SimpleModule("PolymorphicAnimalDeserializerModule");

の代わりに

new SimpleModule("PolymorphicAnimalDeserializerModule",
      new Version(1, 0, 0, null));

animal deserialize()関数で、以下の変更を行います

//Iterator<Entry<String, JsonNode>> elementsIterator =  root.getFields();
Iterator<Entry<String, JsonNode>> elementsIterator = root.fields();

//return mapper.readValue(root, animalClass);
return  mapper.convertValue(root, animalClass); 

これはfasterxml.jacksonで機能します。それでもクラスフィールドに問題がある場合。フィールド名にはjsonと同じ形式を使用します(「_」-アンダースコアを使用)。このように
//mapper.setPropertyNamingStrategy(new CamelCaseNamingStrategy());はサポートされていない可能性があります。

abstract class Animal
{
  public String name;
}

class Dog extends Animal
{
  public String breed;
  public String leash_color;
}

class Cat extends Animal
{
  public String favorite_toy;
}

class Bird extends Animal
{
  public String wing_span;
  public String preferred_food;
}
0
ravi.zombie

ポリモーフィックシリアル化/逆シリアル化を正しく行うには、クラスAnimalの宣言の前に1行だけが必要です。

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
public abstract class Animal {
   ...
}

この行は、完全修飾Javaを保持する「@class」(include = JsonTypeInfo.As.PROPERTY)と呼ばれる、シリアル化時にメタプロパティを追加するか、逆シリアル化時にメタプロパティ(property = "@class")を読み取ります。 _クラス名(use = JsonTypeInfo.Id.CLASS)。

そのため、JSONを(シリアル化なしで)直接作成する場合は、正しい逆シリアル化のために、メタプロパティ「@class」を目的のクラス名で追加することを忘れないでください。

詳細 こちら

0
Marco