JacksonのPolymorphicSerialize/ Deserialize機能は本当にクールです。あるいは、目前の問題にそれを適用する方法を理解できればと思います。http://programmerbruce.blogspot.com/2011/05/deserialize-json-with-jackson-into.htmlに、単純化された問題に適応できなかったというかなり良い記事があります。
一言で言えば、Jackson 2.1.2を取得して、クラス階層を型情報を含むJSON文字列にシリアル化することができます。ただし、Jackson2.1.2でそのJSON文字列を逆シリアル化してクラス階層に戻すことはできません。以下は、この問題を明らかにする単体テストです。
クラス階層は十分に単純です。直接サブクラスが2つしかない基本クラス。さらに、JSON出力は私のJackson @JsonTypeInfoを尊重しているように見え、mapper.writeValueAsStringから信頼できる文字列を生成します
{"type":"dog","name":"King","breed":"Collie"}
しかし、mapper.readValue(jsonOfKing、Animal.class)スタックトレースへの私の呼び出しは...
FAILED: testJacksonSerializeDeserialize
com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class org.rekdev.fasterjacksonwtf.PolymorphismTests$Dog]: can not instantiate from JSON object (need to add/enable type information?)
at [Source: java.io.StringReader@32b3a5a0; line: 1, column: 14]
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:164)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObjectUsingNonDefault(BeanDeserializer.java:400)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:289)
....
これが私のユニットテストです。
import org.testng.annotations.*;
import static org.testng.Assert.*;
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
import com.fasterxml.jackson.databind.*;
public class PolymorphismTests {
@Test
public void testJacksonSerializeDeserialize() throws Exception {
ObjectMapper mapper = new ObjectMapper();
Animal king = new Dog();
king.name = "King";
( (Dog) king ).breed = "Collie";
String jsonOfKing = mapper.writeValueAsString( king );
// JsonMappingException right here!
Animal actualKing = mapper.readValue( jsonOfKing, Animal.class );
assertEquals( king, actualKing );
}
@JsonTypeInfo( use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type" )
@JsonSubTypes( { @Type( value = Cat.class, name = "cat" ), @Type( value = Dog.class, name = "dog" ) } )
abstract class Animal {
public String name;
@Override
public abstract boolean equals( Object obj );
@Override
public abstract int hashCode();
}
class Dog extends Animal {
public String breed;
@Override
public boolean equals( Object obj ) {
if ( this == obj ) {
return true;
}
if ( obj == null ) {
return false;
}
if ( getClass() != obj.getClass() ) {
return false;
}
final Dog that = (Dog) obj;
boolean equals = name.equals( that.name ) && breed.equals( that.breed );
return equals;
}
@Override
public int hashCode() {
int hashCode = name.hashCode() + breed.hashCode();
return hashCode;
}
}
class Cat extends Animal {
public String favoriteToy;
@Override
public boolean equals( Object obj ) {
if ( this == obj ) {
return true;
}
if ( obj == null ) {
return false;
}
if ( getClass() != obj.getClass() ) {
return false;
}
final Cat that = (Cat) obj;
boolean equals = name.equals( that.name ) && favoriteToy.equals( that.favoriteToy );
return equals;
}
@Override
public int hashCode() {
int hashCode = name.hashCode() + favoriteToy.hashCode();
return hashCode;
}
}
}
ObjectMapperで、ObjectMapper.writeValue()によって生成されたJSONをreadValueで処理できないのはなぜですか?