Reputation: 366
I'm trying to deserialize the following structure
{ meta: { keywords: [a, b, c, d]} ... }
other valid structures are
{ meta: { keywords: "a,b,c,d"} ... }
and
{ meta: {keywords: "a"} ...}
I have this classes
public class Data {
@PropertyName("meta")
MetaData meta;
...
}
public class MetaData {
List<String> keywords;
...
}
and a custom deserializer
public static class CustomDeserilizer implements JsonDeserializer<MetaData>{
@Override
public MetaData deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
List<String> keywords = null;
Gson gson = new Gson();
MetaData metaData = gson.fromJson(json, AppMetaData.class);
JsonObject jsonObject = json.getAsJsonObject();
if (jsonObject.has("keywords")) {
JsonElement elem = jsonObject.get("keywords");
if (elem != null && !elem.isJsonNull()) {
if (jsonObject.get("keywords").isJsonArray()) {
keywords = gson.fromJson(jsonObject.get("keywords"), new TypeToken<List<String>>() {
}.getType());
} else {
String keywordString = gson.fromJson(jsonObject.get("keywords"), String.class);
keywords = new ArrayList<String>();
list.addAll(Arrays.asList(keywordString.split(",")));
}
}
}
metaData.setKeywords(keywords);
}
Then I try to apply the deserilizer:
Gson gson = new GsonBuilder()
.registerTypeAdapter(Data.class,new CustomDeserilizer())
.create();
But I get a parsing error , because is trying to deserialize Data instead of MetaData, how can I apply this deserializer to make it work right?
Upvotes: 8
Views: 24876
Reputation: 21
This suppose to achieve what you want easily. You should define an inner static class. You can keep nesting classes to define keywords as class Keywords, etc. Just remember to have a field in the containing class, i.e.
in your inner class have private Keywords keywords;
In your Main class:
Gson gson = new Gson();
Data data = gson.fromJson(SOME_JSON_STRING, Data.class);
In a class called Data:
public class Data {
private Meta meta;
static class Meta{
private String[] keywords;
}
}
Upvotes: 0
Reputation: 12063
Here's a concise solution that leverages Java inheritance to represent the nested structure; and therefore does not need to provide any actual instance member fields (mappings, etc) for capturing the nested String
data that GSON maps.
Step 1: For readability, create an empty object to represent the nested mapping
public class StateRegionCitiesMap extends HashMap<String, List<String>> {
}
Step 2: Add the one line of actual code to do the mapping; no other serialize/deserialize logic to manage
protected void loadContent(JsonObject stateRegionsJsonObject) {
HashMap<String, StateRegionCitiesMap> stateRegionCitiesMap =
mGson.fromJson(
stateRegionsJsonObject,
new TypeToken<HashMap<String, StateRegionCitiesMap>>() {
}.getType()
);
}
Alternatively, you can skip the wrapper class altogether and just directly put <String, List<String>>
in the GSON call. However, I find an explicit object helps to inform/remind whoever is reading the code, what the purpose is.
Example JSON:
The class StateRegionCitiesMap
represents a multi-tier map structure for say:
[US State] -> [State-Region Key] -> [Sub-Region Key] -> CitiesArray[]
"CA": {
"Central CA": {
"Central Valley": [
"FRESNO",
"VISALIA"
],
"Sacramento Area": [
"SACRAMENTO",
"EL DORADO HILLS"
]
},
Upvotes: 0
Reputation: 366
I solved it creating a deserializer for my class Data.
public static class DataDeserilizer implements JsonDeserializer {
@Override
public Data deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
Gson gson = new Gson();
Data data = gson.fromJson(json, Data.class);
JsonObject jsonObject = json.getAsJsonObject();
if (jsonObject.has("meta")) {
JsonElement elem = jsonObject.get("meta");
if (elem != null && !elem.isJsonNull()) {
Gson gsonDeserializer = new GsonBuilder()
.registerTypeAdapter(MetaData.class, new CustomDeserilizer())
.create();
gsonDeserializer.fromJson(jsonObject.get("meta"), Data.class);
}
}
return data;
}
}
And
Gson gson = new GsonBuilder()
.registerTypeAdapter(Data.class,new DataDeserilizer())
.create();
Pretty obvious, but is there a more elegant solution?
Upvotes: 11
Reputation: 611
Firstly, rename your class to meta instead of metadata and make keywords String instead of List.Then use the following to map your JSonString into your object.
Gson gson = new GsonBuilder().create();
Meta meta = gson.from(yourJsonString,Meta.class);
In order to get keywords only, you need this.
JsonObject jsonObject = new JsonObject(yourJSonString);
String data = jsonObject.getJsonObject("meta").getString("keywords");
keywords is a JsonObject not an JsonArray so you can't directly map it onto List. You can split the string to get keywords in an array.
String keywords[] = data.split(",");
Upvotes: 0