2016-12-07 8 views
2

Javaプラットフォームで非直列化する必要がある外部C#システムからJson文字列の形式で応答を取得します。 C#の辞書がJsonの文字列にシリアル化される方法のため、単純ではありません。サンプルマップがjavaとC#でシリアライズされる方法は次のとおりです。C#辞書をJavaマップに変換する汎用jsonデシリアライザ

Java: {"key1":"value1","key2":"value2"} C#: [{"Key":"key1","Value":"value1"},{"Key":"key2","Value":"value2"}]

私たちは、フォーマットは、C#側で変更することはできませんシリアライズ、外部のシステムを制御することはできません。私たちが得た応答には、異なるキー値のペアタイプを持つ多くのマップがあります。だから、私はJsonレスポンス文字列から必要なマップを構築するカスタムジェネリックデシリアライザを作成しようとしています。私はGoogleのGson APIを使用しています。私のパーサコードで

private class DictionaryDeserializerGeneric<K,V> implements JsonDeserializer<Map<K, V>> { 

    @Override 
    public Map<K, V> deserialize(JsonElement json, Type type, 
      JsonDeserializationContext context) throws JsonParseException { 
     JsonArray jArray = (JsonArray)json; 
     Map<K, V> map = new HashMap<K, V>(); 
     for(int i=0;i<jArray.size();i++) { 
      JsonElement element = jArray.get(i); 
      Gson gson = new Gson(); 
      PairGeneric<K,V> pair = gson.fromJson(element, new TypeToken<PairGeneric<K,V>>() {}.getType()); 
      K key = pair.getKey(); 
      V value = pair.getValue(); 
      map.put(key, value); 
     } 
     return map; 
    }  
} 

private class PairGeneric<K,V> { 
    private K Key; 
    private V Value; 
    public K getKey() { 
     return Key; 
    } 
    public V getValue() { 
     return Value; 
    } 
} 

、私は

public static void main(String[] args) { 
    GsonBuilder gsonBuilder = new GsonBuilder(); 
    gsonBuilder.registerTypeAdapter(new TypeToken<ClassA, ClassB>(){}.getType(), new DictionaryDeserializer<ClassA, ClassB>()); 
    gsonBuilder.registerTypeAdapter(new TypeToken<ClassC, ClassD>(){}.getType(), new DictionaryDeserializer<ClassC, ClassD>()); 
    Gson gson = gsonBuilder.create(); 
    String responseStr = getResponse(); 
    ResponseClass responseObj = gson.fromJson(responseStr, ResponseClass.class); 
    doSomeOperation(responseObj); 
} 

public class ResponseClass { 
    Map<ClassA, ClassB> map1; 
    Map<ClassC, ClassC> map2; 
    //Other fields... 
    //Getter and setters... 
} 

問題は種類がどこかで迷子になっていると、私は以下の見ているということである次のようにします。ここでは

は、私が試したものですresponseObjオブジェクトの使用中は例外です。すべてがLinkedHashMapクラスに変換されているようです。私はジェネリックを削除し、複数のデシリアライザ、各キー、値のペアタイプのための1つを作成する場合

java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to ClassA

同じコードが動作します。私はjava型の消去について読んだが、ここでどのように問題を引き起こしているのか理解できなかった。

すべてのキー値タイプに対して機能するカスタムデシリアライザを作成する方法はありますか?

答えて

0

私はあなたが上記の話をされた正確な例外を再現することができませんでしたが、具体的なタイプKV情報がここに存在しないので、私が考えていた最初の事はnew TypeToken<PairGeneric<K,V>>() {}.getType()だった - 彼らはただのプレースホルダこの場所である 。しかし、この場合でも、型トークン(実質的に匿名クラス)の汎用パラメータ化は消去されず、実行時に読み取ることができます。デバッガでこのタイプのトークンの状態を見ると、具体的な型(パーマメータの型名はKV)がありませんが、GSONは解決できる型の逆シリアル化戦略を知っている(実行時に読み込むことができるので、マップがパラメータ化されている場合)。

は、以下の一般的なPairクラスを考えてみましょう:

final class Pair<K, V> { 

    @SerializedName("Key") 
    private final K key = null; 

    @SerializedName("Value") 
    private final V value = null; 

    private Pair() { } 

    K getKey() { return key; } 

    V getValue() { return value; } 

} 

GSONは、このように命名規則Javaでのフィールド名を維持するためにさせる、名前を上書きできますSerializedName注釈をサポートしています。これは着信DTOクラスなので、コード自体がインスタンス化されることはないと想定されており、パブリックコンストラクタは提供されていません。 GSONはコンストラクタやアクセサもなくても動作するので、このクラスは純粋に読み取り専用にすることができます。

一般的な辞書JSONデシリアライザ

デシリアライザは、以下の具体的な種類を意識することなく、デシリアライズすることができます。主な違いは、GSONがPairクラスの戦略を選択できることです。私が理解するところでは、GSONはDTOターゲットタイプに基づいて適切なデシリアライザを解決しようとします(レスポンスオブジェクトはマップmap1map2のパラメータ化されています)。これらの具体的な型はGSONによってさらに処理されます。

final class GenericDictionaryJsonDeserializer<K, V> 
     implements JsonDeserializer<Map<K, V>> { 

    private static final JsonDeserializer<Map<Object, Object>> dictionaryJsonDeserializer = new GenericDictionaryJsonDeserializer<>(); 

    private GenericDictionaryJsonDeserializer() { 
    } 

    static <K, V> JsonDeserializer<Map<K, V>> getGenericDictionaryJsonDeserializer() { 
     @SuppressWarnings({ "unchecked", "rawtypes" }) 
     final JsonDeserializer<Map<K, V>> castDictionaryJsonDeserializer = (JsonDeserializer) dictionaryJsonDeserializer; 
     return castDictionaryJsonDeserializer; 
    } 

    @Override 
    public Map<K, V> deserialize(final JsonElement json, final Type type, final JsonDeserializationContext context) { 
     final JsonArray array = json.getAsJsonArray(); 
     final Map<K, V> result = new LinkedHashMap<>(); 
     for (int i = 0; i < array.size(); i++) { 
      final Pair<K, V> pair = context.deserialize(array.get(i), Pair.class); 
      result.put(pair.getKey(), pair.getValue()); 
     } 
     return result; 
    } 

} 

あなたGsonインスタンスに必要なのはMap.classと、一般的な辞書JSONのデシリアライザが結合されます。いくつかのマップを「Javaスタイル」でデシリアライズしたい場合や、デシリアライザクラスを改良したい場合は、矛盾を避けるため、Mapinterface IDictionary<K,V> extends Map<K,V>という)以外の別のクラスに移行することを検討してください。

ターゲット辞書JSONデシリアライザ

あなたが現在避けるためにしようとしているターゲットのクラスと細かいいる例は、あなたが特定の型にバインドされます代替を使用する場合があります場合。

final class TargetedDictionaryJsonDeserializer<K, V> 
     implements JsonDeserializer<Map<K, V>> { 

    private final Type pairType; 

    private TargetedDictionaryJsonDeserializer(final Type pairType) { 
     this.pairType = pairType; 
    } 

    static <K, V> JsonDeserializer<Map<K, V>> getTargetedDictionaryJsonDeserializer(final TypeToken<Pair<K, V>> typeToken) { 
     return new TargetedDictionaryJsonDeserializer<>(typeToken.getType()); 
    } 

    @Override 
    public Map<K, V> deserialize(final JsonElement json, final Type type, final JsonDeserializationContext context) { 
     final JsonArray array = json.getAsJsonArray(); 
     final Map<K, V> result = new LinkedHashMap<>(); 
     for (int i = 0; i < array.size(); i++) { 
      final Pair<K, V> pair = context.deserialize(array.get(i), pairType); 
      result.put(pair.getKey(), pair.getValue()); 
     } 
     return result; 
    } 

} 

アダプタのこれらのタイプの違いは、後者は具体的なタイプのトークンを受け入れることである(つまり、完全に(下のデモを参照)、コールサイトであなたが宣言している)、およびにタイプトークンタイプを使用していますそれをコンテキストの逆シリアル化に委譲します。それはあなたがそれを必要とする場所で強力な型安全性を保証します。

デモンストレーション

private static final String JSON = "{" + 
     "'map1':[{'Key':'key1','Value':'value1'},{'Key':'key2','Value':'value2'}]," + 
     "'map2':[{'Key':'1','Value':'1.0'},{'Key':'2','Value':'2.0'}]" + 
     "}"; 

private static final TypeToken<Map<String, String>> stringToStringTypeToken = new TypeToken<Map<String, String>>() { 
}; 

private static final TypeToken<Map<Integer, Float>> integerToFloatTypeToken = new TypeToken<Map<Integer, Float>>() { 
}; 

private static final TypeToken<Pair<String, String>> stringToStringPairTypeToken = new TypeToken<Pair<String, String>>() { 
}; 

private static final TypeToken<Pair<Integer, Float>> integerToFloatPairTypeToken = new TypeToken<Pair<Integer, Float>>() { 
}; 

public static void main(final String... args) { 
    dump("by-targeted", getByTargetedTypeAdapter()); 
    dump("by-generic", getByGenericTypeAdapter()); 
    dump("by-mixed", getByMixTypeAdapter()); 
} 

private static Response getByTargetedTypeAdapter() { 
    return new GsonBuilder() 
      .registerTypeAdapter(stringToStringTypeToken.getType(), getTargetedDictionaryJsonDeserializer(stringToStringPairTypeToken)) 
      .registerTypeAdapter(integerToFloatTypeToken.getType(), getTargetedDictionaryJsonDeserializer(integerToFloatPairTypeToken)) 
      .create() 
      .fromJson(JSON, Response.class); 
} 

private static Response getByGenericTypeAdapter() { 
    return new GsonBuilder() 
      .registerTypeAdapter(Map.class, getGenericDictionaryJsonDeserializer()) 
      .create() 
      .fromJson(JSON, Response.class); 
} 

private static Response getByMixTypeAdapter() { 
    return new GsonBuilder() 
      .registerTypeAdapter(Map.class, getGenericDictionaryJsonDeserializer()) 
      .registerTypeAdapter(stringToStringTypeToken.getType(), getTargetedDictionaryJsonDeserializer(stringToStringPairTypeToken)) 
      .create() 
      .fromJson(JSON, Response.class); 
} 

private static final class Response { 

    private final Map<String, String> map1 = null; 
    private final Map<Integer, Float> map2 = null; 

} 

private static void dump(final String name, final Response response) { 
    out.print(">> "); 
    out.println(name); 
    out.print(" "); 
    out.println(response.map1); 
    out.print(" "); 
    out.println(response.map2); 
} 

"対象" モードが具体的なタイプのアダプタを(stringToStringPairTypeTokenintegerToFloatPairTypeTokenがインスタンス化される方法を参照)を渡します。 GsonBuilderはオーバーライドが可能であるため、「混在」テストケースでは、一般的なテスト戦略の後にターゲット戦略が指定されます(そうでない場合、汎用戦略が常に使用されます)。

すべてのテストケースは、JSONを解析し、次の出力印刷:

{KEY1 =値1、値2 = KEY2}
{1 = 1.0、2 = 2.0}

関連する問題