【问题标题】:Mapping Json Array to Java Models将 Json 数组映射到 Java 模型
【发布时间】:2019-03-11 09:37:46
【问题描述】:

我有一个复杂的 json,如 here

我正在尝试将其映射到我的模型类“ChromeJsonModel”中,例如:

Type collectionType = new TypeToken<List<ChromeJsonModel>>(){}.getType();
List<ChromeJsonModel> jsonModelList = (List<ChromeJsonModel>) new Gson().fromJson( jsonPrettyPrintString , collectionType);

但我收到以下错误。

Expected BEGIN_ARRAY but was BEGIN_OBJECT

有什么原因以及我哪里出错了?

【问题讨论】:

  • json 中,一切都是一个(主要)对象,因为您拥有{ ... },因此请使用ChromeJsonModel 而不是List&lt;ChromeJsonModel&gt;
  • 将您的对象列表转换为 JSON 字符串。
  • @MebinJoe 你的意思是这样的:ChromeJsonModel jsonModelList = new Gson().fromJson( jsonPrettyPrintString , ChromeJsonModel.class);我试过这个,但给出了同样的错误

标签: java json gson


【解决方案1】:

您有非常复杂的JSON 有效负载,其中同一属性可能有一个JSON objectJSON array 对象。 Gson 默认情况下不处理这种情况,我们需要为这种one-or-many 属性实现自定义反序列化器。下面我创建了简单的POJO 模型,它代表您的JSON 有效负载:

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
}

如您所见,test-suitetest-caseList-es 属性。让我们为这些属性实现自定义反序列化器:

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&lt;E&gt; 在运行时需要在给定JSON object 的情况下正确反序列化。之后,让我们创建和自定义Gson 实例:

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

如您所见,我们为每个one-to-many 类型注册了两个实例。我们需要使用TypeToken 来正确映射我们的实例。

另见:

版本 2

在使用上述解决方案后,我想出了以下反序列化器:

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

我们不需要定制它。使用$Gson$Types 类,我们可以获得元素的类型并反序列化内部元素。简单用法:

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

上面的代码也应该适合你。

【讨论】:

  • 这对我有用。尽管每次遇到这种情况时都使用自定义反序列化器是一件很痛苦的事情。这是试图解决问题的唯一答案。
  • @Einstein_AB,我很高兴听到它对你有用。我发现更容易接受解决方案。这次我们只需要添加一个解串器。
【解决方案2】:

我认为你可以使用杰克逊。

ObjectMapper mapper = new ObjectMapper();
List<ChromeJsonModel> participantJsonList = mapper.readValue(jsonString, new TypeReference<List<ChromeJsonModel>>(){});

【讨论】:

    【解决方案3】:

    在你json根元素是json对象:

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

    变化:

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

    到:

    ChromeJsonModel jsonModelList = (ChromeJsonModel) ... ;
    

    你可以尝试在那里生成 POJO: http://pojo.sodhanalibrary.com/

    【讨论】:

      猜你喜欢
      • 2016-10-17
      • 2017-02-24
      • 2018-06-19
      • 2017-04-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-29
      相关资源
      最近更新 更多