【问题标题】:Gson Parse Json with array with different object typesGson 使用具有不同对象类型的数组解析 Json
【发布时间】:2013-02-05 17:50:54
【问题描述】:

如何使用 Gson 解析这个 JSON? 我有一个包含多种对象类型的数组,但我不知道我需要创建什么样的对象来保存这个结构。我无法更改 json 消息(我不控制服务器)。

唯一起作用(有点)的类是this

public class Response {
    private List<Object> tr;
    private int results;

    (...)

}

JSON 消息(注意具有多种对象类型的数组。)

{
   "tr":
   [
       {
           "a":
           {
               "userId": "112"
           }
       },
       {
           "b":
           {
               "userId": "123",
               "address":"street dummy" 
           }
       },
       {
           "a":
           {
               "userId": "154"
           }
       }
   ],
"results":3
}

【问题讨论】:

    标签: android json gson


    【解决方案1】:

    Gson 用户指南明确涵盖了这一点:

    https://sites.google.com/site/gson/gson-user-guide#TOC-Serializing-and-Deserializing-Collection-with-Objects-of-Arbitrary-Types

    您有一个对象,其字段 tr 是一个包含任意类型的数组。

    用户指南说明不能直接反序列化这样的结构,建议:

    使用 Gson 的解析器 API(低级流解析器或 DOM 解析器 JsonParser) 解析数组元素,然后使用 Gson.fromJson() 在每个数组元素上。这是首选方法。

    在您的情况下......这实际上取决于该数组中可能有哪些对象。如果它们都将具有相同的内部对象,您会想要执行类似...

    List<MyUserPojo> list = new ArrayList<MyUserPojo>();
    JsonArray array = parser.parse(json).getAsJsonObject().getAsJsonArray("tr");
    for (JsonElement je : array)
    {
        Set<Map.Entry<String,JsonElement>> set = je.getAsObject().entrySet();
        JsonElement je2 = set.iterator().next().getValue();
    
        MyUserPojo mup = new Gson().fromJson(je2, MyUserPojo.class);
        list.add(mup);
    }
    

    当然,这需要在您的实际对象的自定义反序列化器中,该对象将具有 trresults 字段。

    class MyPojo
    {
        List<MyUserPojo> userList;
        int results; 
    }
    
    class MyUserPojo
    {
        String userId;
        String address;
    }
    
    class MyDeserializer implements JsonDeserializer<MyPojo>
    {
        @Override
        public MyPojo deserialize(JsonElement je, Type type, JsonDeserializationContext jdc)
                                  throws JsonParseException
        {
            List<MyUserPojo> list = new ArrayList<MyUserPojo>();
            JsonArray array = je.getAsJsonObject().getAsJsonArray("tr");
            for (JsonElement je2 : array)
            {
                Set<Map.Entry<String,JsonElement>> set = je2.getAsObject().entrySet();
                JsonElement je3 = set.iterator().next().getValue();                 
    
                MyUserPojo mup = new Gson().fromJson(je3, MyUserPojo.class);
                list.add(mup);
            }
    
            MyPojo mp = new MyPojo();
            mp.tr = list;
            mp.results = je.getAsObject().getAsJsonPrimitive("results").getAsInt();
    
            return mp;
        }
    }
    

    现在一切就绪 - 您可以使用该反序列化器并创建您的对象:

    Gson gson = new GsonBuilder()
                    .registerTypeAdapter(MyPojo.class, new MyDeserializer())
                    .build();
    
    MyPojo mp = gson.fromJson(json, MyPojo.class);
    

    如果ab 等很重要……那么,您必须弄清楚这一点。但是上面的内容应该能让你很好地理解处理 JSON 结构需要什么。

    为了完整起见,解决此问题的唯一“hacky”方法是,如果这些类型的数量相当有限,并且内部对象的字段也相当有限。您可以创建一个包含所有可能性的 POJO:

    class MyPojo 
    {
        MySecondPojo a;
        MySecondPojo b;
        ...
        MySecondPojo f;
    }
    
    class MySecondPojo
    {
        String userId;
        String address;
        ...
        String someOtherField;
    }
    

    当 Gson 反序列化 JSON 时,它会将 POJO 中所有缺失的字段设置为 null。您现在可以在 POJO 中将 tr 设为 List 或这些数组。再次强调一下,这真的很老套,而且是错误的做法,但我想我会解释一下直接解析该数组需要什么。

    【讨论】:

    • 大约一小时后,您构建了一个不错的反序列化器。干得好,但恕我直言,仅仅因为您可以做得更好而对所有其他答案投反对票是一种不好的做法。恕我直言,这是一个不赞成投票的理由,但这太愚蠢了,不能浪费代表这样的事情。
    • 老兄,你在说什么?我收到了这些邮件的电子邮件,并在午餐时回复了他们。我刚刚意识到它实际上比最初看起来更痛苦,但最后......是的,这确实有效。
    【解决方案2】:

    我从每个答案中挑选一些东西并这样做:

    响应对象

    public class Response {
        private List<Users> tr;
        private int results;
    
        (...)
    }
    

    通用用户

    public class User {
        public static final int TYPE_USER_A =0;
        public static final int TYPE_USER_B =1;
        private String userId;
        private int type;
        (...)
    }
    

    一个

    public class a extends User {
        private String location;
        (...) 
    }
    

    B

    public class b extends User { 
        private String adress;   
        (...)
    }
    

    解析方法

    private Response buildResponseObject(String response) {
            Response tls = new Response();
            List<Users> users = new ArrayList<users>();
            User u;
    
            try {
                JSONObject object = new JSONObject(response);
                tls.setResults(object.getInt("results"));
                JSONArray array = object.getJSONArray("tr");
    
                for (int i = 0; i < array.length(); i++) {
                    JSONObject trs = array.getJSONObject(i);
    
                    if (trs.has("a")) {
                        String json = trns.getString("a");
                        A a = new Gson().fromJson(json,A.class);
                        a.setType(User.TYPE_USER_A);
                        users.add(a);
    
                    } else if (trs.has("b")) {
                        String json = trs.getString("b");
                        B b= new Gson().fromJson(json,B.class);
                        B.setType(User.TYPE_USER_B);
                        users.add(b);
                    }
                }
    
                tls.setUsers(users);
    
            } catch (JSONException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return tls;
        }
    

    这并不像我想要的那样优雅,将原生 JsonObjects 与 Gson 方法混合,但对我有用。

    【讨论】:

      【解决方案3】:

      在这里试试这个代码:

      public class Address {
          public String userId;
          public String address;
          // ...
      }
      
      public class Response {
          private HashMap<String, Address> tr;
          private int results;
          // ...
      }
      

      用法:

      String json = "{\n  \"tr\":\n  {\n    \"a\": {\n       \"userId\": \"112\"\n    },\n    \"b\": {\n       \"userId\": \"123\",\n       \"address\":\"street dummy\"\n    },\n    \"c\": {\n       \"userId\": \"154\"\n    }\n  },\n  \"results\":3\n}";
      
      Response users = new Gson().fromJson(json, Response.class);
      

      如您所见,我需要修改结构:

      {
        "tr":
        {
          "a": {
             "userId": "112"
          },
          "b": {
             "userId": "123",
             "address":"street dummy"
          },
          "c": {
             "userId": "154"
          }
        },
        "results":3
      }
      

      但不幸的是,我没有设法允许多个键。现在我不知道如何解决这个问题。

      【讨论】:

      • 解析时出错。这可能是我的代码中的某种错误,但无论哪种方式,第二个“a”不会因为它们具有相同的键而删除第一个对象吗?
      • 写完我的答案后,我问自己。我认为这个类需要用别的东西来交换。我会寻找替代方案。你的例外是什么?
      • 也许Hashtable 可以解决这个问题。我会为此写一个测试用例。
      • @Laranjeiro 请查看我的回答。
      • 我无法更改 JSON 消息。
      【解决方案4】:

      我认为此链接可能会对您有所帮助: https://sites.google.com/site/gson/gson-user-guide#TOC-Collections-Examples

      基本上,为您的“对象”(我猜是用户类型)创建一个类,然后使用 Gson 的反序列化代码,如下所示:

      Type collectionType = new TypeToken<Collection<User>>(){}.getType();
      Collection<User> users= gson.fromJson(json, collectionType);
      

      【讨论】:

        【解决方案5】:

        您可以为 json 对象创建相应的 java 类。整数、字符串值可以按原样映射。 Json可以这样解析-

        Gson gson = new GsonBuilder().create();
        Response r = gson.fromJson(jsonString, Response.class);
        

        这是一个例子-http://rowsandcolumns.blogspot.com/2013/02/url-encode-http-get-solr-request-and.html

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多