2017-01-26 17 views
1

私は、強く型付けされたフィールドと緩やかに型付けされたフィールドを持つデータ構造を持っています。これらのフィールドの中には、深いネストされたコレクションがあります。私はダイナミック型を使用したジャクソンのポリモーフィック・デシリアライズ

public static class Parent { 
    private String prop; 
    private Child1 child1; 
    private MyMap<?> map; 
} 

public static class Child1 { 
    private int anInt; 
} 

public static class MyMap<T> extends HashMap<String, T> implements Map<String, T> { 
} 

にこれをマッピングする

例JSON

{ 
    "prop": "Hello",    //strongly-typed 
    "child1": { 
    "anInt": -1 
    }, 
    "map": {      // here magic begins 
    "JustString": "JustValue", // we may store any Object in this map 
    "Item_With_Type": { 
     "@type": "MyMap",   // some of them tell their type and we need to rely on it 
     "Custom": "Value" 
    }, 
    "List_With_All_Child1": { 
     "@type": "MyMap[]",  // lists define the type of all values in it in this way 
     "@values": [ 
     { 
      "Key": "Value",  // MyMap is a Map 
      "Child1": {   // of <? extends Object> 
      "anInt": 2 
      } 
     }, 
     { 
      "Key": "Value" 
     } 
     ] 
    } 
    } 
} 

(アクセサは省略)

基本的に私は、ジャクソンが種類ごとについて質問しますデータバインダーのようなものを必要としますフィールド保存コンテキストの型を解決しようとすると、このデータバインダーでアプリケーション固有のものが見つからなかった場合、Jacksonはデフォルトの型解決にフォールバックする必要があります。

これを実現する方法はありますか?

+2

投稿しないでくださいのコードへのリンクあなたの質問はスタックオーバーフローでここにあります。 – CraigR8806

+0

'ObjectMapper'を試してみてください –

答えて

0

ジャクソンと遊んだ後、私は次のような解決策に出た。私のためにうまく動作します。

まず、我々は唯一のjava.lang.Objectのためのシリアライズ/ deserilizationを入力処理するカスタムTypeResolverを作成する多型

@JsonTypeResolver(MyTypeResolver.class) 
@JsonTypeIdResolver(MyTypeIdResolver.class) 
@JsonTypeInfo(use = JsonTypeInfo.Id.CUSTOM, include = JsonTypeInfo.As.PROPERTY, property = "@type") 
public interface ObjectMixin { 

} 

ObjectMapper mapper = new ObjectMapper(); 
mapper.configure(SerializationFeature.INDENT_OUTPUT, true); 
mapper.addMixIn(Object.class, ObjectMixin.class); 

すべてを作ります。

public class MyTypeResolver extends StdTypeResolverBuilder { 

    @Override 
    public TypeSerializer buildTypeSerializer(SerializationConfig config, JavaType baseType, Collection<NamedType> subtypes) { 
     return useForType(baseType) ? super.buildTypeSerializer(config, baseType, subtypes) : null; 
    } 

    @Override 
    public TypeDeserializer buildTypeDeserializer(DeserializationConfig config, JavaType baseType, Collection<NamedType> subtypes) { 
     return useForType(baseType) ? super.buildTypeDeserializer(config, baseType, subtypes) : null; 
    } 

    public boolean useForType(JavaType t) { 
     return t.isJavaLangObject(); 
    } 
} 

タイプIdResolverはIDマジックを処理します。この例では、すべてがハードコードされていますが、実際のコードでは、それははるかに良いと思っています。 :)

public class MyTypeIdResolver extends TypeIdResolverBase { 

    @Override 
    public String idFromValue(Object value) { 
     return getId(value); 
    } 

    @Override 
    public String idFromValueAndType(Object value, Class<?> suggestedType) { 
     return getId(value); 
    } 

    @Override 
    public JsonTypeInfo.Id getMechanism() { 
     return JsonTypeInfo.Id.CUSTOM; 
    } 

    private String getId(Object value) { 
     if (value instanceof ListWrapper.MyMapListWrapper) { 
      return "MyMap[]"; 
     } 

     if (value instanceof ListWrapper.Child1ListWrapper) { 
      return "Child1[]"; 
     } 

     if (value instanceof ListWrapper && !((ListWrapper) value).getValues().isEmpty()) { 
      return ((ListWrapper) value).getValues().get(0).getClass().getSimpleName() + "[]"; 
     } 

     return value.getClass().getSimpleName(); 
    } 

    @Override 
    public JavaType typeFromId(DatabindContext context, String id) throws IOException { 
     if (id.endsWith("[]")) { 
      if (id.startsWith("Child1")) { 
       return TypeFactory.defaultInstance().constructParametricType(ListWrapper.class, Child1.class); 
      } 
      if (id.startsWith("MyMap")) { 
       return TypeFactory.defaultInstance().constructSpecializedType(TypeFactory.unknownType(), ListWrapper.MyMapListWrapper.class); 
      } 
     } 
     if (id.equals("Child1")) { 
      return TypeFactory.defaultInstance().constructSpecializedType(TypeFactory.unknownType(), Child1.class); 
     } 
     if (id.equals("MyMap")) { 
      return TypeFactory.defaultInstance().constructSpecializedType(TypeFactory.unknownType(), MyMap.class); 
     } 

     return TypeFactory.unknownType(); 
    } 
} 

は、私がListWrapperクラスとサブクラスを持っている{"@type: "...", "@values": ...}リストを処理することができます。 Todo:カスタム非直列化ロジックを使用してこれを再実装します。

public class ListWrapper<T> {  
    @JsonProperty("@values") 
    private List<T> values; 

    public static class MyMapListWrapper extends ListWrapper<MyMap> { 
    } 

    public static class Child1ListWrapper extends ListWrapper<Child1> { 
    } 
} 

これは、サブクラスの作成をスキップすることが可能ですが、その後の型情報は、一つ一つの要素に追加されます。 java.lang.*もちろん、クラスには型情報はありません。

モデルは以下のとおりです。

public class Parent { 
    private String prop; 
    private Child1 child1; 
    private MyMap map; 
} 

public class Child1 { 
    private int anInt; 
} 

テストコード:

@Test 
public void shouldDoTheTrick() throws IOException { 
    ObjectMapper mapper = new ObjectMapper(); 
    mapper.configure(SerializationFeature.INDENT_OUTPUT, true); 
    mapper.addMixIn(Object.class, ObjectMixin.class); 

    Parent parent = new Parent("Hello", new Child1(-1), new MyMap() {{ 
     put("JustString", "JustValue"); 
     put("List_With_All_MyMaps", new ListWrapper.MyMapListWrapper(new ArrayList<MyMap>() {{ 
      add(new MyMap() {{ 
       put("Key", "Value"); 
       put("object", new Child1(2)); 
      }}); 
      add(new MyMap() {{ 
       put("Key", "Value"); 
      }}); 
     }})); 
     put("List_With_All_Child1", new ListWrapper.Child1ListWrapper(new ArrayList<Child1>() {{ 
      add(new Child1(41)); 
      add(new Child1(42)); 
     }})); 
    }}); 

    String valueAsString = mapper.writeValueAsString(parent); 

    Parent deser = mapper.readValue(valueAsString, Parent.class); 
    assertEquals(parent, deser); 
} 

JSON出力:

{ 
    "prop" : "Hello", 
    "child1" : { 
    "anInt" : -1 
    }, 
    "map" : { 
    "JustString" : "JustValue", 
    "List_With_All_MyMaps" : { 
     "@type" : "MyMap[]", 
     "@values" : [ { 
     "Key" : "Value", 
     "object" : { 
      "@type" : "Child1", 
      "anInt" : 2 
     } 
     }, { 
     "Key" : "Value" 
     } ] 
    }, 
    "List_With_All_Child1" : { 
     "@type" : "Child1[]", 
     "@values" : [ { 
     "anInt" : 41 
     }, { 
     "anInt" : 42 
     } ] 
    } 
    } 
} 

UPD:実際の実装例https://github.com/sdl/dxa-web-application-java/commit/7a36a9598ac2273007806285ea4d854db1434ac5

関連する問題