Mapping Json Array to Java Models

后端 未结 3 641
野性不改
野性不改 2020-12-12 02:53

I have a complex json as in here

I am trying to map this in my model class \'ChromeJsonModel\' like :

Type collectionType = new TypeToken

        
相关标签:
3条回答
  • 2020-12-12 03:37

    You have very complex JSON payload where the same property could have one JSON object or JSON array of objects. Gson does not handle this case by default and we need to implement custom deserialiser for this kind of one-or-many properties. Below I created simple POJO model which represents your JSON payload:

    class TestResponse {
    
        @SerializedName("test-run")
        private TestRun testRun;
    
        // other properties, getters, setters, toString
    }
    
    class TestRun {
    
        @SerializedName("test-suite")
        private List<TestSuite> testSuite;
    
        // other properties, getters, setters, toString
    }
    
    class TestSuite {
        private String result;
        private double duration;
    
        @SerializedName("test-suite")
        private List<TestSuite> testSuites;
    
        @SerializedName("test-case")
        private List<TestCase> testCases;
    
        // other properties, getters, setters, toString
    }
    
    class TestCase {
    
        private String fullname;
    
        // other properties, getters, setters, toString
    }
    

    As you can see test-suite and test-case are List-es properties. Let's implement custom deserialiser for these properties:

    class OneOrManyJsonDeserializer<E> implements JsonDeserializer<List<E>> {
    
        private final Class<E> clazz;
    
        public OneOrManyJsonDeserializer(Class<E> clazz) {
            this.clazz = clazz;
        }
    
        @Override
        public List<E> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
            if (json instanceof JsonArray) {
                final JsonArray array = (JsonArray) json;
                final int size = array.size();
                if (size == 0) {
                    return Collections.emptyList();
                }
                final List<E> suites = new ArrayList<>(size);
                for (int i = 0; i < size; i++) {
                    suites.add(context.deserialize(array.get(i), clazz));
                }
    
                return suites;
            }
    
            E suite = context.deserialize(json, clazz);
            return Collections.singletonList(suite);
        }
    }
    

    Class<E> is required in runtime to deserialise properly given JSON object. After that, let's create and customise Gson instance:

    import com.google.gson.Gson;
    import com.google.gson.GsonBuilder;
    import com.google.gson.JsonArray;
    import com.google.gson.JsonDeserializationContext;
    import com.google.gson.JsonDeserializer;
    import com.google.gson.JsonElement;
    import com.google.gson.JsonParseException;
    import com.google.gson.annotations.SerializedName;
    import com.google.gson.reflect.TypeToken;
    
    import java.io.File;
    import java.io.FileReader;
    import java.lang.reflect.Type;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    
    public class GsonApp {
    
        public static void main(String[] args) throws Exception {
            File jsonFile = new File("./resource/test.json").getAbsoluteFile();
    
            Type testCaseListType = new TypeToken<List<TestCase>>() {}.getType();
            Type testSuiteListType = new TypeToken<List<TestSuite>>() {}.getType();
            Gson gson = new GsonBuilder()
                    .registerTypeAdapter(testCaseListType, new OneOrManyJsonDeserializer<>(TestCase.class))
                    .registerTypeAdapter(testSuiteListType, new OneOrManyJsonDeserializer<>(TestSuite.class))
                    .setPrettyPrinting()
                    .create();
    
            TestResponse response = gson.fromJson(new FileReader(jsonFile), TestResponse.class);
            System.out.println(response);
        }
    }
    

    As, you can see, we registered two instances for each one-to-many types. We need to use TypeToken to have correct mapping of our instances.

    See also:

    • Parsing JSON with GSON, object sometimes contains list sometimes contains object
    • Gson uses TypeAdapter or Json Deserializer to convert data from an error list to an empty list - example with TypeAdapter

    VERSION 2

    After playing with above solution I came up with below deserialiser:

    class OneOrManyJsonDeserializer implements JsonDeserializer<List<?>> {
    
        @Override
        public List<?> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
            final Type elementType = $Gson$Types.getCollectionElementType(typeOfT, List.class);
    
            if (json instanceof JsonArray) {
                final JsonArray array = (JsonArray) json;
                final int size = array.size();
                if (size == 0) {
                    return Collections.emptyList();
                }
    
                final List<?> suites = new ArrayList<>(size);
                for (int i = 0; i < size; i++) {
                    suites.add(context.deserialize(array.get(i), elementType));
                }
    
                return suites;
            }
    
            Object suite = context.deserialize(json, elementType);
            return Collections.singletonList(suite);
        }
    }
    

    We do not need to customise it. Using $Gson$Types class we can get type of element and deserialise internal element. Simple usage:

    import com.google.gson.Gson;
    import com.google.gson.GsonBuilder;
    import com.google.gson.JsonArray;
    import com.google.gson.JsonDeserializationContext;
    import com.google.gson.JsonDeserializer;
    import com.google.gson.JsonElement;
    import com.google.gson.JsonParseException;
    import com.google.gson.annotations.SerializedName;
    import com.google.gson.internal.$Gson$Types;
    
    import java.io.File;
    import java.io.FileReader;
    import java.lang.reflect.Type;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    
    public class GsonApp {
    
        public static void main(String[] args) throws Exception {
            File jsonFile = new File("./resource/test.json").getAbsoluteFile();
    
            Gson gson = new GsonBuilder()
                    .registerTypeAdapter(List.class, new OneOrManyJsonDeserializer())
                    .setPrettyPrinting()
                    .create();
    
            TestResponse response = gson.fromJson(new FileReader(jsonFile), TestResponse.class);
            System.out.println(response);
        }
    }
    

    Above code should also work for you.

    0 讨论(0)
  • 2020-12-12 03:37

    I think you can use jackson.

    ObjectMapper mapper = new ObjectMapper();
    List<ChromeJsonModel> participantJsonList = mapper.readValue(jsonString, new TypeReference<List<ChromeJsonModel>>(){});
    
    0 讨论(0)
  • 2020-12-12 03:37

    In you json root element is json object:

    {    <---- HERE YOU HAVE "OBJECT"
    
      "test-run": {
        "duration": 508.56199999999995,
        "result": "Passed",
        ...
       }
    
    }
    

    Change:

    List<ChromeJsonModel> jsonModelList = (List<ChromeJsonModel>) ... ;
    

    to:

    ChromeJsonModel jsonModelList = (ChromeJsonModel) ... ;
    

    You can try to generate POJO there: http://pojo.sodhanalibrary.com/

    0 讨论(0)
提交回复
热议问题