Search code examples
javagsonmappingunmarshallingjsonobjectrequest

Json string to Object mapping with dynamic values


I am consuming Thirdparty jsonString, I am trying to parse the json but sometimes JSON object "RadarReports" is an list and sometimes it object.

{"RadarReports": {
    "executionTime": "135",
"RadarReport": {
        "abc": "1116591",
        "name": "abc",
        "id": "2019050311582056119",
        "ownerId": "xyz"
    },
"size" :"1"
}}

=================

  {"RadarReports": {
        "executionTime": "113",
        "RadarReport": [
            {
                 "abc": "1116591",
            "name": "abc",
            "id": "2019050311582056119",
            "ownerId": "xyz"
            },
            {
            "abc": "1116591",
            "name": "abc",
            "id": "2019050311582056119",
            "ownerId": "xyz"
            },
        ]
    "size" : "2"
    }}

I tried below to parse but failing when single object came into picture, need to accept both single and list of objects.

@Data
public class Radarreports {
    private int size;
    private ArrayList<RadarreportSet> RadarReportSet;
    private ArrayList<RadarReport> RadarReport;
}

@Data
public
class ReportsResponse {
    Radarreports RadarReports;
}

URL url = new URL(queryUrl);
BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()));
Gson gson = new GsonBuilder().create();
ReportsResponse radarReports = gson.fromJson(br, ReportsResponse.class);

Solution

  • You could solve this with a custom TypeAdapterFactory which creates an adapter which first peeks at the type of the JSON data and then adds special handling where the JSON object is not wrapped in an JSON array:

    // Only intended for usage with @JsonAdapter
    class SingleObjectOrListAdapterFactory implements TypeAdapterFactory {
        @Override
        public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
            // Note: Cannot use getDelegateAdapter due to https://github.com/google/gson/issues/1028
            TypeAdapter<T> listAdapterDelegate = gson.getAdapter(type);
            TypeAdapter<JsonObject> jsonObjectAdapter = gson.getAdapter(JsonObject.class);
    
            return new TypeAdapter<T>() {
                @Override
                public void write(JsonWriter out, T value) throws IOException {
                    listAdapterDelegate.write(out, value);
                }
    
                @Override
                public T read(JsonReader in) throws IOException {
                    if (in.peek() == JsonToken.BEGIN_OBJECT) {
                        // Wrap JSON object in a new JSON array before parsing it
                        JsonObject jsonObject = jsonObjectAdapter.read(in);
                        JsonArray jsonArray = new JsonArray();
                        jsonArray.add(jsonObject);
    
                        return listAdapterDelegate.fromJsonTree(jsonArray);
                    } else {
                        return listAdapterDelegate.read(in);
                    }
                }
            };
        }
    }
    

    The factory can then be specified for the affected field with @JsonAdapter:

    @JsonAdapter(SingleObjectOrListAdapterFactory.class)
    private ArrayList<RadarReport> RadarReport;