java 反序列化json时跳过根元素

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12004338/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 07:13:40  来源:igfitidea点击:

Skip root element while deserializing json

javajsongson

提问by Mateusz Chromiński

How should I deserialize following JSON to skip root element and parse just the inner part of this JSON. I'd like to avoid creating additional, 3rd class Root, which would include only MapWrapperfield.

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

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

So I'd like to have only these two classes:

所以我只想有这两个类:

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

class MyMapEntry {
    String name;
}

采纳答案by Byter

you can use GSONLibrary for this.

您可以GSON为此使用库。

Below code will solve your problem.

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

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);

回答by yottabrain

Consider the following JSON:

考虑以下 JSON:

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

The DTO for this JSON without the root element

此 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;
    }
}

Convert from/to JSON using the following methods (you can either keep this within DTO or some other help class)

使用以下方法从/到 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;
}

回答by NameSpace

My answer is late to this party.

我的回答迟到了这个聚会。

Once we parse the Json, the container is always going to be a JsonObject subclass of JsonElement. Thus, if we want to skip it, we just need to cast it to its subclass and grab the field holding our inner class.

一旦我们解析了 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);

Note: JsonObject and JSONObject are different classes (use the com.google.Json import).

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

You could generalize this answer more such that you wouldn't need to know the name of the inner class. You would do this by simply getting the one-and-only field of the container object. However, I see no way to do this other than starting up the iterator, there is no getValue(atIndex) method I can see, and I think starting an iterator is probably less efficient than simply looking up the field by name (but could be wrong).

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

The iterator method looks like:

迭代器方法如下所示:

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

回答by Byter

This is the optimal code to do it in one pass.

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

MapWrapperclass

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;
    }
}

MyMapEntryclass

MyMapEntry班级

public class MyMapEntry {

    String name;

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

The Custom Deserializer

自定义解串器

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);
    }   
}

Register it with GSON

用 GSON 注册

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

Now deserialise with following code

现在使用以下代码反序列化

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

回答by Gustav Barkefors

You could deserialize it into a Map<String, MapWrapper>.

您可以将其反序列化为Map<String, MapWrapper>.

回答by Timo

Inspired by Gustav Carlson's idea I decided to expand it to a concrete sample. Here's a junit test that tests parsing this JSON as Map.

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

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);
}