I want to parse a json object containing dynamic field type using Gson:
{
"rows":
[
{
"id": "1",
"interventions": [
{
"type": "type1",
"label": "label 1"
},
{
"type": "type2",
"label": ["label 1","label 2"]
},
{
"type": "type3",
"label": "label 3",
}
]
}
]
}
As you can see that the "label" field can be String or list of strings.
I wrote a customized deserializer to handle this issue, it works if the "interventions" field has only one element (regardless the "label" field is a string or list):
{"rows":
[
{
"id": "1",
"interventions": [
{
"type": "type1",
"label": "label 1"
}
]
}
]
}
But always throws com.google.gson.JsonArray cannot be cast to com.google.gson.JsonPrimitive exception if there are more than one "interventions" element.
Here is the customized deserializer:
public class CustomDeserializer implements JsonDeserializer<InterventionsModel> {
@Override
public InterventionsModel deserialize(JsonElement je, Type type, JsonDeserializationContext jdc) throws JsonParseException {
if(je != null && je.getAsJsonObject()!=null) {
JsonPrimitive jp = je.getAsJsonObject().getAsJsonPrimitive("label");
if (jp != null && jp.isString()) {
String label = jp.getAsString();
List<String> list = new ArrayList<String>(1);
list.add(label);
InterventionsModel interventionsModel = new InterventionsModel();
interventionsModel.setLabel(list);
return interventionsModel;
}
}
return new Gson().fromJson(je, InterventionsModel.class);
}
}
In the calling method:
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(InterventionsModel.class, new CustomDeserializer());
builder.setPrettyPrinting();
Gson gson = builder.create();
The classes for the objects are:
public class ResultsModel {
private List<RowModel> rows;
//getter and setter ..
}
public class RowModel {
private String id;
private List<InterventionsModel> interventions;
//getter and setter
}
public class InterventionsModel {
private String type;
private List<String> label;
//setter and getter
}
Could someone please help?
You don't have to create a custom de-serializer for the entire InterventionsModel
.
Instead, just apply the @JsonAdapter
annotation to the List<String> label
field
public class InterventionsModel {
private String type;
@JsonAdapter(LabelsDeserializer.class)
private List<String> label;
// Setters and getters
}
And create a de-serializer for a List<String>
type
public class LabelsDeserializer implements JsonDeserializer<List<String>> {
@Override
public List<String> deserialize(
final JsonElement json,
final Type typeOfT,
final JsonDeserializationContext context) {
// Check if the JSON object is an array or a primitive value
if (json.isJsonArray()) {
// Multiple Strings elements
final JsonArray jsonArray = json.getAsJsonArray();
final List<String> labels = new ArrayList<>(jsonArray.size());
for (final JsonElement jsonElement : jsonArray) {
labels.add(jsonElement.getAsString());
}
return labels;
}
// Single String element
return Collections.singletonList(json.getAsString());
}
}
You also have a mismatch between the Java model's field type
and the JSON document field intervention_type
.
As a general advice, try to always customize the shortest/smallest portion of your code, and try to build generic ones. Customizations carry a lot of work to be maintained over time.
For Gson 2.6.*
, use
public class LabelsDeserializer extends TypeAdapter<List<String>> {
@Override
public void write(
final JsonWriter out,
final List<String> labels) throws IOException {
if (labels.size() == 1) {
out.value(labels.get(0));
return;
}
out.beginArray();
for (final String l : labels) {
out.value(l);
}
out.endArray();
}
@Override
public List<String> read(final JsonReader in) throws IOException {
final JsonToken peek = in.peek();
if (peek.equals(JsonToken.BEGIN_ARRAY)) {
final List<String> labels = new ArrayList<>();
in.beginArray();
while (in.hasNext()) {
labels.add(in.nextString());
}
in.endArray();
return labels;
}
return Collections.singletonList(in.nextString());
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.