多態的な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.に感謝します。
約束どおり、アノテーションを使用してポリモーフィックオブジェクトをシリアル化/逆シリアル化する方法の例を示しています。この例は、読んでいたチュートリアルの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;
}
ご覧のとおり、Cat
とDog
には特別なものはありません。それらについて知っているのはabstract
クラスAnimal
だけなので、逆シリアル化するときは、次のテストでわかるように、Animal
とObjectMapper
をターゲットにすると、実際のインスタンスが返されます。
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
お役に立てれば、
ホセ・ルイス
ジャクソンライブラリを介した多相性のシリアル化/逆シリアル化を有効にする簡単な方法は、ジャクソンオブジェクトマッパー(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
次に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;
}
ポリモーフィックシリアル化/逆シリアル化を正しく行うには、クラス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」を目的のクラス名で追加することを忘れないでください。
詳細 こちら