Пропустить корневой элемент при десериализации json

Как мне десериализовать следующий JSON, чтобы пропустить корневой элемент и проанализировать только внутреннюю часть этого JSON. Я хотел бы избежать создания дополнительного, 3-го класса 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;
}

person Mateusz Chromiński    schedule 17.08.2012    source источник


Ответы (6)


для этого вы можете использовать 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);
person Byter    schedule 17.08.2012
comment
да, это можно сделать, написав собственный JsonDeserializer и зарегистрировав его в GSON. - person Byter; 17.08.2012
comment
любой пример будет оценен - person Mateusz Chromiński; 17.08.2012

Рассмотрим следующий JSON:

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

DTO для этого JSON без корневого элемента

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;
}
person yottabrain    schedule 13.10.2015
comment
Хорошее решение по мне - person Riskhan; 22.02.2018

Это оптимальный код, чтобы сделать это за один проход.

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);
person Byter    schedule 17.08.2012
comment
это заставляет меня создать дополнительный класс, что было проблемой с самого начала. При этом лучше создать класс Wraper с одним полем типа MapWrapper с именем root. - person Mateusz Chromiński; 17.08.2012
comment
@Mathew На самом деле нет необходимости в классе Wrapper - person Byter; 17.08.2012
comment
без оболочки он создает нулевой объект, потому что ctx.deserialize вызовет метод MyDeserializer.deserialize для создания объекта MapWrapper - person Mateusz Chromiński; 17.08.2012
comment
В конце концов, я думаю, что в данном конкретном случае создание дополнительного класса-оболочки для корневого элемента является самым чистым решением. Внутренняя структура может быть скрыта внутри класса поставщика данных, так что это не проблема для качества кода (очень простой класс держателя данных с очень определенной функцией). Спасибо за ваши усилия - я принимаю ваше другое решение, потому что оно самое короткое и, кроме того, небольшие накладные расходы на обработку - лучшая альтернатива. - person Mateusz Chromiński; 19.08.2012

Мой ответ опоздал на эту вечеринку.

Как только мы проанализируем Json, контейнер всегда будет подклассом JsonObject класса JsonElement. Таким образом, если мы хотим пропустить его, нам просто нужно привести его к его подклассу и захватить поле, содержащее наш внутренний класс.

    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();
person NameSpace    schedule 26.08.2016

Вы можете десериализовать его в Map<String, MapWrapper>.

person Gustav Barkefors    schedule 17.08.2012
comment
Как вы десериализуете данные (или планируете)? (Какая-то конкретная структура?) - person Gustav Barkefors; 17.08.2012

Вдохновленный идеей Густава Карлсона, я решил расширить ее до конкретного образца. Вот тест junit, который проверяет синтаксический анализ этого JSON как карты.

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);
}
person Timo    schedule 26.04.2016