Пропуснете основния елемент, докато десериализирате 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

Можете също да го направите както по-долу.

$name = "";

function sayMyName(){
    getName(); // inner function generates $name value
    //set $name variable inside getName() function.
    echo $name; // results to undefined
}

sayMyName();
- 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 import).

Бихте могли да обобщите този отговор повече, така че да не е необходимо да знаете името на вътрешния клас. Бихте направили това, като просто получите едно-и-единственото поле на контейнерния обект. Въпреки това не виждам друг начин да направя това освен стартиране на итератора, няма метод 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