4

次のような応答テンプレートを持つデータを解析しています。

{
  response: {
    data: {}
  }
  meta: {
    errors: []
    success: 1
  }
}

特定の応答ごとに、「データ」フィールドに動的フィールドが入力されますが、他のすべてのキー (メタなど) は同じままです。例えば:

クラスA

data: {
  foo: ""
}

クラスB

data: {
  bar: 3
}

クラスをモデル化し、Gson を使用してこのデータを表現するにはどうすればよいですか? 現在、私は持っています:

public class BaseResponse {
  @SerializedName("meta") public Meta meta;

  public class Meta {
    @SerializedName("errors") public ArrayList<Error> errors;
    @SerializedName("success") public int success;
  }
}

public class ClassA extends BaseResponse {
  @SerializedName("foo") public String foo;
}

public class ClassB extends BaseResponse {
  @SerializedName("bar") public int bar;
}

残念ながら、解析時に foo フィールドと bar フィールドは null 値を返します。たとえば、次のように使用します。

Gson.fromJson(jsonString, ClassA.class);

これは、「foo」フィールドと「bar」フィールドが内部参照であるためだと思います。

4

1 に答える 1

7

問題を理解しているので、目的のポリモーフィックデシリアライズを提供するにはカスタム処理が必要です。使用するタイプの決定は、特定のJSON要素名の存在に基づいています。(JSONには型情報が含まれていないため、 Jacksonなどの別のシリアル化APIに切り替えると、より単純なソリューションは提供されません。)

以下は、私が採用する可能性のあるアプローチを示しています。

package com.stackoverflow.q15578106;

import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;

public class Foo
{
  public static void main(String[] args)
  {
    /*
      {
        "response": {"data": {"foo": "FOO"}},
        "meta": {"errors": [], "success": 1}
      }
     */
    String input1 = "{\"response\": {\"data\": {\"foo\": \"FOO\"}},\"meta\": {\"errors\": [], \"success\": 1}}"; 

    /*
      {
        "response": {"data": {"bar": 42}},
        "meta": {"errors": [], "success": 1}
      }
     */
    String input2 = "{\"response\": {\"data\": {\"bar\": 42}},\"meta\": {\"errors\": [], \"success\": 1}}";

    processInput(input1);
    // {"response":{"data":{"foo":"FOO"}},"meta":{"errors":[],"success":1}}

    processInput(input2);
    // {"response":{"data":{"bar":42}},"meta":{"errors":[],"success":1}}
  }

  static void processInput(String jsonInput)
  {
    DataDeserializer dataDeserializer = new DataDeserializer();
    dataDeserializer.registerDataType("foo", A.class);
    dataDeserializer.registerDataType("bar", B.class);

    Gson gson = new GsonBuilder().registerTypeAdapter(Data.class, dataDeserializer).create();

    BaseResponse response = gson.fromJson(jsonInput, BaseResponse.class);
    System.out.println(new Gson().toJson(response)); 
  }
}

class DataDeserializer implements JsonDeserializer<Data>
{
  Map<String, Class<? extends Data>> dataTypeRegistry = new HashMap<String, Class<? extends Data>>();

  void registerDataType(String jsonElementName, Class<? extends Data> javaType)
  {
    dataTypeRegistry.put(jsonElementName, javaType);
  }

  @Override
  public Data deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
  {
    JsonObject jsonObject = json.getAsJsonObject();
    for (String elementName : dataTypeRegistry.keySet())
    {
      if (jsonObject.has(elementName))
      {
        Class<? extends Data> dataType = dataTypeRegistry.get(elementName);
        return context.deserialize(jsonObject, dataType);
      }
    }
    throw new RuntimeException("Oops");
  }
}

class BaseResponse
{
  Response response;
  Meta meta;
}

class Meta
{
  List<String> errors;
  int success;
}

class Response
{
  Data data;
}

class Data
{

}

class A extends Data
{
  String foo;
}

class B extends Data
{
  int bar;
}
于 2013-03-23T23:47:03.127 に答える