【问题标题】:How do I parse a nested JSON array of object with a custom Gson deserializer?如何使用自定义 Gson 反序列化器解析嵌套的 JSON 对象数组?
【发布时间】:2017-05-05 17:58:19
【问题描述】:

大家好。我在理解这一点时遇到了一些麻烦。我有一个如下所示的 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"
          }
        }
      ]
    }

我的改造电话是:

 @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&lt;IncludedModel&gt;?出于某种原因,我可以为Object 创建一个自定义JsonDeserializerTypeAdapter,但是当涉及到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 列路径 $.included[0] 处为 BEGIN_OBJECT

我怀疑这是因为我的 Retrofit 响应是 &lt;PositionResponse&gt;,它是 JsonObject

总结我的问题:如何使用我自己的自定义类型适配器反序列化 List&lt;IncludeModel&gt; 对象,同时记住我的改造服务的响应类型是 PositionResponse?非常感谢您的患者和回答。

【问题讨论】:

  • 为什么需要自定义适配器?如果您的模型与 json 匹配,则通常不需要。
  • @nasch 因为如果你查看包含对象,类型会动态变化,服务器返回的是其中的其他嵌套对象,比如 JSONAPI 格式的“关系”标签,我会有很多难以维护的模型类,因此最好将它们映射到自定义模型中。如果可能的话,也出于学习目的。

标签: android gson


【解决方案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通过反射;如果通过switch 进行检查,JsonToken 会更好(顺便说一下,enums 是单例,使用引用相等== 比较它们是完全合法的)等)。

所以,可以这样实现:

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&lt;String, List&lt;Element&gt;&gt;,但你决定)。

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:ST先生

【讨论】:

  • 非常感谢!.. 还有您发送的其他链接中的信息。我会尝试一下并告诉你。
  • 抱歉回复晚了。它有效!!.. 非常感谢您的帮助和信息。学到了新东西!
猜你喜欢
  • 1970-01-01
  • 2019-03-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多