2016-03-22 20 views
9

次のシナリオを想像してみてください。 (例えば、Foo<Something>プロパティを持つクラスを非直列化するために)Foo<T>の具体的な型を、Barで、逆シリアル化時に使用する必要があります(たとえば、Tがその中にSomethingであることを知る必要があります)。特定の場合)。ジェネリック型のためのカスタムデシリアライザの作成方法私はFooのためのカスタム・ジャクソンのデシリアライザを書きたい</p> <pre><code>class <T> Foo<T> { .... } class Bar { Foo<Something> foo; } </code></pre> <p>:

このようなデシリアライザはどのように記述しますか?ジャクソンはタイプされたコレクションとマップでそれを行うので、それを行うことが可能でなければなりません。

明確化:

問題の解決に2部があるようだ。Bar内のプロパティfooの宣言された型を取得し、Foo<Somehting>

2をデシリアライズするためにそれを使用)

1)デシリアライズ時に、プロパティfooを内部クラスBarにデシリアライズしていることを確認して、ステップ1を完了します。

1と2はどのようにして完成しますか?

+0

あなたは*宣言していることを見つけるためにリフレクションを使用することができます'foo'のタイプは' Foo 'です。それはそれです。それがあなたの最後の手段であるかどうかを言うためにジャクソンについて私は知らない。 ['Field#getGenericType'](http://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Field.html#getGenericType--) – Radiodef

答えて

23

汎用タイプのカスタムJsonDeserializerを実装できます。この汎用タイプには、ContextualDeserializerも実装されています。例えば

、我々は一般的な値が含まれている次のような単純なラッパー型があるとしますのインスタンスに

{ 
    "name": "Alice", 
    "age": 37 
} 

:我々は次のようになりますJSONをデシリアライズしたい

public static class Wrapper<T> { 
    public T value; 
} 

をこのようなクラス:

public static class Person { 
    public Wrapper<String> name; 
    public Wrapper<Integer> age; 
} 

ContextualDeserializerフィールドのジェネリック型パラメータに基づいて、Personクラスの各フィールドに固有のデシリアライザを作成できます。これにより、名前を文字列としてデシリアライズし、年齢を整数としてデシリアライズすることができます。

完全デシリアライザは、次のようになります。これは、ジャクソンで最初に呼び出されるよう

public static class WrapperDeserializer extends JsonDeserializer<Wrapper<?>> implements ContextualDeserializer { 
    private JavaType valueType; 

    @Override 
    public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException { 
     JavaType wrapperType = property.getType(); 
     JavaType valueType = wrapperType.containedType(0); 
     WrapperDeserializer deserializer = new WrapperDeserializer(); 
     deserializer.valueType = valueType; 
     return deserializer; 
    } 

    @Override 
    public Wrapper<?> deserialize(JsonParser parser, DeserializationContext ctxt) throws IOException { 
     Wrapper<?> wrapper = new Wrapper<>(); 
     wrapper.value = ctxt.readValue(parser, valueType); 
     return wrapper; 
    } 
} 

それは、まずここcreateContextualを見ることが最善です。 BeanProperty(たとえばWrapper<String>)のフィールドのタイプを読み取り、最初のジェネリックタイプのパラメータ(たとえばString)を抽出します。新しいデシリアライザを作成し、内側の型をvalueTypeとして格納します。

新しく作成されたデシリアライザでdeserializeが呼び出されると、その値をラッパータイプ全体ではなく内部型としてデシリアライズし、デシリアライズされた値を含む新しいWrapperを返すことができます。

このカスタムデシリアライザを登録するために、我々はそれを含むモジュールを作成し、そのモジュールを登録する必要があります。

SimpleModule module = new SimpleModule() 
     .addDeserializer(Wrapper.class, new WrapperDeserializer()); 

ObjectMapper objectMapper = new ObjectMapper(); 
objectMapper.registerModule(module); 

私たちはその後、上からの例JSONをデシリアライズしようとした場合、私たちが見ることができます期待どおりに動作する:

Person person = objectMapper.readValue(json, Person.class); 
System.out.println(person.name.value); // prints Alice 
System.out.println(person.age.value); // prints 37 

デシリアライザはJackson documentationでどのように機能するか、コンテキストについてのいくつかの詳細があります。

+1

このポストをありがとう。信じられないほど私が望むものを達成するのに役立ちます。 私は、Googleの他の誰かが来た場合に、ルート値で動作するいくつかの変更を行いました。 https://gist.github.com/darylteo/a7be65b539c0d8d3ca0de94d96763f33 –

+1

本当に助けていただきありがとうございました コンテキストタイプ自体は一般的である可能性があります。その場合、プロパティはnullになります.ctxt.getContextualTypeを使用してJsonDeserializerを作成する必要があります –

1

ターゲット自体は、プロパティはそのためにあなたがDeserializationContextからvalueTtypeを取得する必要があります、nullになります一般的なタイプである場合:*

@Override 
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException { 
    if (property == null) { // context is generic 
     JMapToListParser parser = new JMapToListParser(); 
     parser.valueType = ctxt.getContextualType().containedType(0); 
     return parser; 
    } else { // property is generic 
     JavaType wrapperType = property.getType(); 
     JavaType valueType = wrapperType.containedType(0); 
     JMapToListParser parser = new JMapToListParser(); 
     parser.valueType = valueType; 
     return parser; 
    } 
} 
+0

受け入れられた回答にNPEを取得している人は、おそらくこの解決策が必要です。ありがとうございました - 私はあなたが私を救ってどれくらいの時間を知っています。 – Martin

関連する問題

 関連する問題