Reputation: 2832
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 MapWrapper
field.
{
"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;
}
Upvotes: 14
Views: 16981
Reputation: 10177
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.
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).
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).
The iterator method looks like:
JsonElement jsonQuery = ((JsonObject) jsonContainer).entrySet().iterator()
.next().getValue();
Upvotes: 3
Reputation: 3404
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.
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);
}
Upvotes: 0
Reputation: 2615
Consider the following JSON:
{"authorization":{"username":"userabc", "password":"passabc"}}
The DTO for this JSON without the root element
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)
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;
}
Upvotes: 3
Reputation: 1132
This is the optimal code to do it in one pass.
MapWrapper
class
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
class
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
new GsonBuilder().registerTypeAdapter(MapWrapper.class,new MyDeserialiser()).create()
Now deserialise with following code
String json; // your jsonString
MapWrapper result = ConvertJsonToObject.getFromJSON(json,MapWrapper.class);
Upvotes: 2
Reputation: 1132
you can use GSON
Library for this.
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);
Upvotes: 10