如何使用自定义Gson反序列化器解析嵌套的JSON对象数组?

时间:2017-05-05 17:58:20

标签: android gson

美好的一天。理解这一点我有点麻烦。我有一个看起来像这样的JSON:

  {
      "data": [
        {
          "id": "43",
          "type": "position",
          "attributes": {
            "address-id": "1",
            "employer-id": "11"
          }
        }
      ],
      "included": [
        {
          "id": "1",
          "type": "address",
          "attributes": {
            "line-1": "21 london london",
            "line-2": "",
            "line-3": "",
            "locality": "",
            "region": "London",
            "post-code": "",
            "country": "UK",
            "latitude": "",
            "longitude": ""
          }
        },
        {
          "id": "11",
          "type": "employer",
          "attributes": {
            "title": "Mr",
            "first-name": "S",
            "last-name": "T"
          }
        }
      ]
    }

我的Retrofit电话是:

 @GET("/api/positions")
 Single<PositionResponse> getPosition();

我的PositionResponse课程:

public class PositionResponse {

        @SerializedName("data")
        @Expose
        private List<DataResponse> data;
        @SerializedName("included")
        @Expose
        private List<IncludedModel> included;

        public List<DataResponse> getData() {
            return data;
        }

        public void setData(List<DataResponse> data) {
            this.data = data;
        }

        public List<IncludedModel> getIncluded() {
            return included;
        }

        public void setIncluded(List<IncludedModel> included) {
            this.included = included;
        }

        }
    }

现在想象它有更多的数据。如何创建自定义TypeAdapterJsonDeserializer来解析List<IncludedModel>?出于某种原因,我可以为JsonDeserializer创建自定义TypeAdapterObject,但是当涉及List时,我似乎无法得到那个工作。

我的TypeAdapter如下:

  public class IncludedTypeAdapter extends TypeAdapter<ArrayList<IncludedModel>> {

        @Override
        public void write(JsonWriter out, ArrayList<IncludedModel> value) throws IOException {

        }

        @Override
        public ArrayList<IncludedModel> read(JsonReader in) throws IOException {
            ArrayList<IncludedModel> list = new ArrayList<>();
            IncludedModel model = new IncludedModel();
            Gson gson = new Gson();
            in.beginArray();
            String id = null;
            //in.beginObject();
            while(in.hasNext()){
                JsonToken nextToken = in.peek();

                if(JsonToken.BEGIN_OBJECT.equals(nextToken)){
                    in.beginObject();

                } else if(JsonToken.NAME.equals(nextToken)){

                    if(JsonToken.NAME.name().equals("id")){
                        id = in.nextString();
                        model.setId(id);

                    } else if(JsonToken.NAME.name().equals("type")){
                        String type = in.nextString();
                        model.setMytype(type);

                        switch (type) {
                            case BaseModelType.Employer:
                                EmployerResponse employer = gson.fromJson(in, EmployerResponse.class);
                                model.setEmployer(employer);
                                break;
                        }
                    }
                }
            }
            list.add(model);

            return list;
        }

我注册了我的Gson:

  GsonBuilder gsonBuilder = new GsonBuilder();
      gsonBuilder.registerTypeAdapter(IncludeModel.class, new IncludedTypeAdapter());
     //gsonBuilder.registerTypeAdapter(new IncludedTypeAdapter());
      gsonBuilder.serializeNulls();
      Gson gson = gsonBuilder.create();

      return gson;

我通过GsonConverterFactory进行了改造注册。

我得到了:

  

预期BEGIN_ARRAY但是在第1行第6292行路径为BEGIN_OBJECT $ .included [0]

我怀疑是因为我的改造回复是<PositionResponse>,这是JsonObject

总结我的问题:如何使用我自己的自定义类型适配器反序列化List<IncludeModel>对象,同时我的Retrofit服务的响应类型为PositionResponse?非常感谢您的患者和答案。

1 个答案:

答案 0 :(得分:1)

如果您使用JsonDeserializer使用JSON树模型,这很容易。纯类型适配器有点过分(我认为,RuntimeTypeAdapterFactory也是如此,因为它仍然是面向树的),在JSON文档的最简单的情况下你可以使用这样的东西(你可以找到一个my yesterday answer中有类似的方法有更多的解释,但你的情况略有不同)。

我假设你想要这样的映射:

abstract class Element {

    final String id = null;

    private Element() {
    }

    static final class Address
            extends Element {

        @SerializedName("line-1") final String line1 = null;
        @SerializedName("line-2") final String line2 = null;
        @SerializedName("line-3") final String line3 = null;
        @SerializedName("locality") final String locality = null;
        @SerializedName("region") final String region = null;
        @SerializedName("post-code") final String postCode = null;
        @SerializedName("country") final String country = null;
        @SerializedName("latitude") final String latitude = null;
        @SerializedName("longitude") final String longitude = null;

        private Address() {
        }

        @Override
        public String toString() {
            return country + " " + region;
        }

    }

    static final class Employer
            extends Element {

        @SerializedName("title") final String title = null;
        @SerializedName("first-name") final String firstName = null;
        @SerializedName("last-name") final String lastName = null;

        private Employer() {
        }

        @Override
        public String toString() {
            return title + ' ' + firstName + ' ' + lastName;
        }

    }

    static final class Position
            extends Element {

        @SerializedName("address-id") final String addressId = null;
        @SerializedName("employer-id") final String employerId = null;

        private Position() {
        }

        @Override
        public String toString() {
            return '(' + addressId + ';' + employerId + ')';
        }

    }

}

你所要做的只是:

  • 确定预期的对象类型;
  • “对齐”JSON树(如果它确实符合您的需要);
  • 只需通过反序列化上下文将反序列化工作委托给Gson(您的示例不能很好地执行:您再次失去原始配置实例化Gson;重做所有Gson可以开箱即用:列表和POJO通过反思; JsonToken如果通过switch进行检查会更好(顺便说一句,enum是单身人士,使用引用等式==)比较它们是完全合法的,等)。

所以,它可以通过以下方式实现:

final class ElementJsonDeserializer
        implements JsonDeserializer<Element> {

    private static final JsonDeserializer<Element> elementJsonDeserializer = new ElementJsonDeserializer();

    private ElementJsonDeserializer() {
    }

    static JsonDeserializer<Element> getElementJsonDeserializer() {
        return elementJsonDeserializer;
    }

    @Override
    public Element deserialize(final JsonElement jsonElement, final Type type, final JsonDeserializationContext context)
            throws JsonParseException {
        final JsonObject jsonObject = jsonElement.getAsJsonObject();
        final String typeCode = jsonObject.getAsJsonPrimitive("type").getAsString();
        final Class<? extends Element> clazz;
        switch ( typeCode ) {
        case "address":
            clazz = Element.Address.class;
            break;
        case "employer":
            clazz = Element.Employer.class;
            break;
        case "position":
            clazz = Element.Position.class;
            break;
        default:
            throw new JsonParseException("Unrecognized type: " + typeCode);
        }
        reattach(jsonObject, "attributes");
        return context.deserialize(jsonElement, clazz);
    }

    private static void reattach(final JsonObject parent, final String property) {
        final JsonObject child = parent.getAsJsonObject(property);
        parent.remove(property); // remove after we're sure it's a JSON object
        copyTo(parent, child);
    }

    private static void copyTo(final JsonObject to, final JsonObject from) {
        for ( final Entry<String, JsonElement> e : from.entrySet() ) {
            to.add(e.getKey(), e.getValue());
        }
    }

}

当然,您可以重构上述内容以提取策略以实现策略设计模式以重用它。把它们放在一起:

final class Response {

    final List<Element> data = null;
    final List<Element> included = null;

}

(上面看起来像Map<String, List<Element>>,但你决定了。)

private static final Gson gson = new GsonBuilder()
        .registerTypeAdapter(Element.class, getElementJsonDeserializer())
        .create();

public static void main(final String... args)
        throws IOException {
    try ( final JsonReader jsonReader = getPackageResourceJsonReader(Q43811168.class, "data.json") ) {
        final Response response = gson.fromJson(jsonReader, Response.class);
        dump(response.data);
        dump(response.included);
    }
}

private static void dump(final Iterable<Element> elements) {
    for ( final Element e : elements ) {
        System.out.print(e.getClass().getSimpleName());
        System.out.print(" #");
        System.out.print(e.id);
        System.out.print(": ");
        System.out.println(e);
    }
}

输出:

  

位置#43:(1; 11)
  地址#1:英国伦敦
  雇主#11:S先生