2017-05-05 62 views
0

美好的一天。我在理解这一点时遇到了一些麻烦。我有一个JSON看起来像这样:如何使用自定义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" 
      } 
     } 
     ] 
    } 

我的改造呼叫:

@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>?出于某种原因,我可以为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但BEGIN_OBJECT第1分6292列路径$ .included [0]

我怀疑是因为我的改造响应<PositionResponse>这是一个JsonObject

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

+1

为什么你需要一个自定义适配器?如果你的模型匹配json,你通常不需要一个。 – nasch

+1

@nasch,因为如果你看包含对象,类型会动态改变,服务器返回的是其他嵌套对象,就像JSONAPI格式的“关系”标记一样,我会有很多难以维护的模型类所以最好将它们映射到自定义模型中。也用于学习目的,如果可能的话。 – irobotxxx

回答

0

如果您使用使用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:ST先生

+0

非常感谢!也为您发送的其他链接中的信息。我会试试看,并让你知道。 – irobotxxx

+1

对于迟到的回复感到抱歉。它工作!! ..大量感谢您的帮助和信息。学到了新东西! – irobotxxx