【问题标题】:Skip root element while deserializing json反序列化 json 时跳过根元素
【发布时间】:2012-08-17 10:48:35
【问题描述】:

我应该如何反序列化以下 JSON 以跳过根元素并仅解析此 JSON 的内部部分。我想避免创建额外的第三类Root,它只包含MapWrapper 字段。

{
    "root": {
        "language": "en",
        "map": {
            "k1": {
                "name": "n1",
            },
            "k2": {
                "name": "n2",
            }
        }
    }
}

所以我希望只有这两个类:

class MapWrapper {
    private String language;
    private Map<String, MyMapEntry> map;
}

class MyMapEntry {
    String name;
}

【问题讨论】:

    标签: java json gson


    【解决方案1】:

    您可以为此使用 GSON 库。

    以下代码将解决您的问题。

    public class ConvertJsonToObject {
    
        private static Gson gson = new GsonBuilder().create();
    
        public static final <T> T getFromJSON(String json, Class<T> clazz) {
            return gson.fromJson(json, clazz);
        }
    
        public static final <T> String toJSON(T clazz) {
            return gson.toJson(clazz);
        }
    }
    
    String json; // your jsonString
    Map<String,Object> r = ConvertJsonToObject.getFromJSON(json,Map.class);
    String innerJson = ConvertJsonToObject.toJson(r.get("root"));
    MapWrapper _r = ConvertJsonToObject.getFromJSON(innerJson,MapWrapper.class);
    

    【讨论】:

    • 是的,可以通过编写自定义 JsonDeserializer 并将其注册到 GSON 来实现。
    • 任何例子都将不胜感激
    【解决方案2】:

    考虑以下 JSON:

    {"authorization":{"username":"userabc", "password":"passabc"}}
    

    没有根元素的 JSON 的 DTO

    public class Authorization {
        private String username;
        private String password;
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        // Add a container for the root element
        public static class Container {
            public Authorization authorization;
        }
    }
    

    使用以下方法从/到 JSON 转换(您可以将其保存在 DTO 或其他一些帮助类中)

    public String toJson(Authorization authorization) {
        Gson gson = new Gson();
        Authorization.Container container = new Authorization.Container();
        container.authorization = authorization;
        return gson.toJson(container);
    }
    
    public Authorization fromJson(String json) {
        Gson gson = new Gson();
        Authorization.Container container = gson.fromJson(json, Authorization.Container.class);
        return container.authorization;
    }
    

    【讨论】:

      【解决方案3】:

      这是一次完成的最佳代码。

      MapWrapper

      public class MapWrapper {
          private String language;
          private Map<String, MyMapEntry> map;
      
          public MapWrapper(String language, Map<String, MyMapEntry> map) {
              this.language = language;
              this.map = map;
          }
      }
      

      MyMapEntry

      public class MyMapEntry {
      
          String name;
      
          public MyMapEntry(String name) {
              this.name = name;
          }
      }
      

      自定义反序列化器

      public class MyDeserialiser  implements JsonDeserializer<MapWrapper>
      {
      
          @Override
          public MapWrapper deserialize(JsonElement json, Type typeOfT,
              JsonDeserializationContext ctx) throws JsonParseException {
      
              JsonObject _global = json.getAsJsonObject();
              _global = _global.get("root").getAsJsonObject();
      
              JsonPrimitive lang = (JsonPrimitive) _global.get("language");
              JsonElement map = _global.get("map");
              Map<String, MyMapEntry> inMap = new LinkedHashMap<String, MyMapEntry>();
              for (Entry<String, JsonElement> entry : map.getAsJsonObject()
                      .entrySet()) {
                  MyMapEntry _m = new MyMapEntry(entry.getValue().toString());
                  inMap.put(entry.getKey(), _m);
              }
              return new MapWrapper(lang.getAsString(), inMap);
          }   
      }
      

      向 GSON 注册

      new GsonBuilder().registerTypeAdapter(MapWrapper.class,new MyDeserialiser()).create()
      

      现在用下面的代码反序列化

      String json; // your jsonString
      MapWrapper result = ConvertJsonToObject.getFromJSON(json,MapWrapper.class);
      

      【讨论】:

      • 这让我创建了额外的类,这是一开始的问题。这样做,最好创建 Wraper 类,其中一个名为 root 的 MapWrapper 类型字段。
      • @Mathew 实际上根本不需要 Wrapper 类
      • 没有包装它会创建空对象,导致ctx.deserialize 会调用MyDeserializer.deserialize 方法来创建MapWrapper 对象
      • 毕竟我认为在这种特殊情况下为根元素创建额外的包装类是最干净的解决方案。内部结构可以隐藏在数据提供者类中,因此对代码质量没有任何问题(非常简单的数据持有者类,具有非常指定的功能)。感谢您的努力 - 我接受您的另一种解决方案,因为它是最短的,而且处理开销很小 - 最好的选择。
      【解决方案4】:

      我的回答迟到了。

      一旦我们解析了 Json,容器总是会是 JsonElement 的 JsonObject 子类。因此,如果我们想跳过它,我们只需要将它转换为它的子类并获取包含我们内部类的字段。

          String response = ....;
      
          Gson gson = new Gson();
      
          JsonParser p = new JsonParser();
          JsonElement jsonContainer = p.parse(response);
          JsonElement jsonQuery = ((JsonObject) jsonContainer).get("query");
      
          MyQuery query = gson.fromJson(jsonQuery, MyQuery.class);
      

      注意:JsonObject 和 JSONObject 是不同的类(使用 com.google.Json 导入)。

      您可以更概括这个答案,这样您就不需要知道内部类的名称。您可以通过简单地获取容器对象的唯一字段来做到这一点。但是,除了启动迭代器之外,我认为没有其他方法可以做到这一点,我看不到 getValue(atIndex) 方法,而且我认为启动迭代器可能比简单地按名称查找字段效率低(但可能是错误)。

      迭代器方法如下:

          JsonElement jsonQuery = ((JsonObject) jsonContainer).entrySet().iterator()
                                  .next().getValue();
      

      【讨论】:

        【解决方案5】:

        您可以将其反序列化为Map&lt;String, MapWrapper&gt;

        【讨论】:

        • 您如何反序列化数据(或计划)? (任何特定的框架?)
        【解决方案6】:

        受 Gustav Carlson 的想法的启发,我决定将其扩展为具体示例。这是一个测试将此 JSON 解析为 Map 的 junit 测试。

        public static class MapWrapper {
            private String language;
            private Map<String, MyMapEntry> map;
        }
        
        public static class MyMapEntry {
            String name;
        }
        
        @Test
        public void testParsing() {
            String json = "{\n" +
                    "    \"root\": {\n" +
                    "        \"language\": \"en\",\n" +
                    "        \"map\": {\n" +
                    "            \"k1\": {\n" +
                    "                \"name\": \"n1\"\n" +
                    "            },\n" +
                    "            \"k2\": {\n" +
                    "                \"name\": \"n2\"\n" +
                    "            }\n" +
                    "        }\n" +
                    "    }\n" +
                    "}";
            Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
            Type type = new TypeToken<Map<String, MapWrapper>>(){}.getType();
            Map<String, MapWrapper> parsed = gson.fromJson(json, type);
            MapWrapper mapWrapper = parsed.get("root");
            Assert.assertEquals("en", mapWrapper.language);
            Assert.assertEquals("n2", mapWrapper.map.get("k2").name);
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2016-10-02
          • 1970-01-01
          • 2012-07-27
          • 1970-01-01
          • 2012-02-08
          • 1970-01-01
          相关资源
          最近更新 更多