Gson: JsonArray cannot be cast to JsonPrimitive error

Karen :

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?

LppEdd :

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());
    }
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=144847&siteId=1