Пропустить корневой элемент при десериализации 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;
}
Ответы
Ответ 1
вы можете использовать GSON
Library для этого.
Ниже код поможет решить вашу проблему.
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);
Ответ 2
Это оптимальный код для этого за один проход.
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);
Ответ 3
Мой ответ опоздал на эту вечеринку.
Как только мы разобраем 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();
Ответ 4
Рассмотрим следующий 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;
}
Ответ 5
Вы можете десериализовать его в Map<String, MapWrapper>
.
Ответ 6
Вдохновленный идеей Густава Карлсона, я решил расширить его до конкретного образца. Здесь 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);
}