简体   繁体   English

无法使用 Gson 将复杂/动态 JSON 反序列化为 Java 类

[英]Unable to deserialize complex/dynamic JSON to Java classes using Gson

I've been trying to deserialize a JSON to Java classes using Gson, but the JSON structure is too complex for me to handle. I've been trying to deserialize a JSON to Java classes using Gson, but the JSON structure is too complex for me to handle. The JSON looks like this (I've trimmed some of it because of repetitions): JSON 看起来像这样(由于重复,我已经修剪了一些):

{
   "results":[
      {
         "openEHR-EHR-CLUSTER.encounter_channel.v0/items[at0001]/value<DV_TEXT>":{
            "type":"DV_TEXT",
            "name":{
               "en":"Encounter channel"
            },
            "attrs":[
               "value"
            ]
         },
         "openEHR-EHR-CLUSTER.monitoring_reason.v0/items[at0001]/value<DV_TEXT>":{
            "type":"DV_TEXT",
            "name":{
               "en":"Monitoring reason"
            },
            "attrs":[
               "value"
            ]
         }
      },
      {
         "163eee06-83a4-4fd8-bf65-5d6a3ef35ac5":{
            "d5760d01-84dd-42b2-8001-a69ebaa4c2df":{
               "date":"2020-08-06 09:45:31",
               "cols":[
                  {
                     "type":"DV_TEXT",
                     "path":"openEHR-EHR-CLUSTER.encounter_channel.v0/items[at0001]/value<DV_TEXT>",
                     "values":[
                        {
                           "instanceTemplatePath":"prova_de_conceito.en.v1/context/other_context[at0001]/items[archetype_id=openEHR-EHR-CLUSTER.encounter_channel.v0](0)/items[at0001](0)/value",
                           "value":"null"
                        }
                     ]
                  },
                  {
                     "type":"DV_TEXT",
                     "path":"openEHR-EHR-CLUSTER.monitoring_reason.v0/items[at0001]/value<DV_TEXT>",
                     "values":[
                        {
                           "instanceTemplatePath":"prova_de_conceito.en.v1/context/other_context[at0001]/items[archetype_id=openEHR-EHR-CLUSTER.monitoring_reason.v0](1)/items[at0001](0)/value",
                           "value":"null"
                        }
                     ]
                  }
               ]
            },
            "fb366b72-d567-4d23-9f5f-356fc09aff6f":{
               "date":"2020-08-06 10:02:26",
               "cols":[
                  {
                     "type":"DV_TEXT",
                     "path":"openEHR-EHR-CLUSTER.encounter_channel.v0/items[at0001]/value<DV_TEXT>",
                     "values":[
                        {
                           "instanceTemplatePath":"prova_de_conceito.en.v1/context/other_context[at0001]/items[archetype_id=openEHR-EHR-CLUSTER.encounter_channel.v0](0)/items[at0001](0)/value",
                           "value":"Consulta presencial"
                        }
                     ]
                  },
                  {
                     "type":"DV_TEXT",
                     "path":"openEHR-EHR-CLUSTER.monitoring_reason.v0/items[at0001]/value<DV_TEXT>",
                     "values":[
                        {
                           "instanceTemplatePath":"prova_de_conceito.en.v1/context/other_context[at0001]/items[archetype_id=openEHR-EHR-CLUSTER.monitoring_reason.v0](1)/items[at0001](0)/value",
                           "value":"Consulta"
                        }
                     ]
                  }
               ]
            }
         }
      }
   ],
   "pagination":{
      "max":20,
      "offset":0,
      "nextOffset":20,
      "prevOffset":0
   },
   "timing":"475 ms"
}

The main JSON object has three fields: results , pagination and timing .主要的JSON object有三个字段: results , paginationtiming I can deserialize the pagination and timing just fine, as they always have the same structure.我可以很好地反序列化paginationtiming ,因为它们总是具有相同的结构。 I cannot properly deserialize the results though.我无法正确反序列化results

results is always a list of two different objects. results始终是两个不同对象的列表。 The second object, in particular, is the most complex one, as its field names are not static.特别是第二个 object 是最复杂的一个,因为它的字段名称不是 static。 The UUID name references always change on each API response. UUID 名称引用总是在每个 API 响应上更改。 For instance, the field named "163eee06-83a4-4fd8-bf65-5d6a3ef35ac5" might have another id in the next JSON response.例如,名为"163eee06-83a4-4fd8-bf65-5d6a3ef35ac5"的字段可能在下一个 JSON 响应中具有另一个 id。 Therefore, I cannot give it a proper field name in the corresponding Java class.因此,我无法在相应的 Java class 中给它一个正确的字段名称。 The same goes for "d5760d01-84dd-42b2-8001-a69ebaa4c2df" and "fb366b72-d567-4d23-9f5f-356fc09aff6f" in this case.在这种情况下, "d5760d01-84dd-42b2-8001-a69ebaa4c2df""fb366b72-d567-4d23-9f5f-356fc09aff6f"也是如此。

Any ideas on how to properly deserialize this kind of JSON using Gson?关于如何使用 Gson 正确反序列化这种 JSON 的任何想法? I've tried a couple of different approaches, but nothing has truly worked so far.我尝试了几种不同的方法,但到目前为止还没有真正奏效。

In most recent attempt I tried to use the JsonDeserializer approach in order to differentiate the type of objects in the results list.在最近的尝试中,我尝试使用 JsonDeserializer 方法来区分results列表中的对象类型。 My current implementation looks like this (getters and setters were hidden because of space):我当前的实现看起来像这样(getter 和 setter 因为空间而被隐藏):

QueryResponse.java查询响应.java

public class QueryResponse {
    private List<Map<String, ResultInterface>> results;
    private Pagination pagination;
    private String timing;
}

Pagination.java分页.java

public class Pagination {
    private Integer max;
    private Integer offset;
    private Integer nextOffset;
    private Integer previousOffset;
}

ResultInterface.java结果接口.java

public interface ResultInterface {

}

ElementDefinition.java元素定义.java

public class ElementDefinition implements ResultInterface {
    private String type;
    private Name name;
    private List<String> attrs;
}

Name.java名称.java

public class Name {
    private String en;
    private String es;
}

Compositions.java作文.java

public class Compositions implements ResultInterface {
    private Map<String, Composition> compositions;
}

Composition.java组成。java

public class Composition {
    private String date;
    private List<Col> cols;
}

Col.java Col.java

public class Col {
    private String type;
    private String path;
    private List<Value> values;
}

Value.java值.java

public class Value {
    private String instanceTemplatePath;
    private String value;
    private String magnitude;
    private String units;
    private String code;
    private String terminology_id;
}

ResultInterfaceDeserializer.java ResultInterfaceDeserializer.java

public class ResultInterfaceDeserializer implements JsonDeserializer<ResultInterface> {
    
    @Override
    public ResultInterface deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        JsonObject jObject = (JsonObject) json;
        JsonElement typeObj = jObject.get("type");

        if (typeObj != null) {
            return context.deserialize(json, ElementDefinition.class);
        } else {
            return context.deserialize(json, Compositions.class);
        }
    }
}

I'm calling Gson like this:我这样称呼 Gson :

GsonBuilder builder = new GsonBuilder();
        
builder.registerTypeAdapter(ResultInterface.class, new ResultInterfaceDeserializer());
Gson gson = builder.create();
        
QueryResponse queryResponse = gson.fromJson(externalJsonResponse, QueryResponse.class);

The problem with this implementation is that there is nothing named compositions in the JSON structure, thus the Compositions.java class is not correctly identified.此实现的问题在于 JSON 结构中没有任何命名compositions ,因此没有正确识别Compositions.java class。 I know I have to use Java structures like Map<String, SomeObject> , but the problem is that there are too many dynamically named Json fields here, and I cannot "grab" them if they have no fixed name identifier.我知道我必须使用 Java 结构,例如Map<String, SomeObject> ,但问题是这里有太多动态命名的 Json 字段,如果它们没有固定的名称标识符,我无法“抓取”它们。

UPDATE更新

I managed to find a solution.我设法找到了解决方案。 I'd say it's actually a workaround and probably not the most clean or elegant solution.我想说这实际上是一种解决方法,可能不是最干净或优雅的解决方案。 The problem with my current implementation was that I was trying to "grab" a JSON field called compositions when in fact it didn't exist.我当前实现的问题是我试图“抓取”一个名为compositions的 JSON 字段,而实际上它并不存在。 So, I decided to manipulate the JSON and add that field myself (in the code).所以,我决定操纵 JSON 并自己添加该字段(在代码中)。 I changed the deserializer class to:我将解串器 class 更改为:

public class ResultInterfaceDeserializer implements JsonDeserializer<ResultInterface> { 
    public String encloseJsonWithCompositionsField(JsonElement json) {
        return "{\"compositions\":" + json.toString() + "}";
    }
    
    @Override
    public ResultInterface deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        JsonObject jObject = (JsonObject) json;
        
        if (jObject.get("type") != null) {
            return context.deserialize(json, ElementDefinition.class);
        } else {
            JsonElement jsonWithCompositionsField = new JsonParser().parse(encloseJsonWithCompositionsField(json));
            return context.deserialize(jsonWithCompositionsField, Compositions.class);
        }
    }
}

With this change, I can now "grab" the compositions field and get the data in Java POJOs.通过此更改,我现在可以“抓取”组合字段并获取 Java POJO 中的数据。

You could probably solve this by registering an additional JsonDeserializer for Compositions :您可以通过为Compositions注册一个额外的 JsonDeserializer 来解决这个问题:

public class CompositionsDeserializer implements JsonDeserializer<Compositions> {
    public static final CompositionsDeserializer INSTANCE = new CompositionsDeserializer();
    
    private CompositionsDeserializer() { }
    
    @Override
    public Compositions deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        Compositions compositions = new Compositions();
        Map<String, Composition> compositionsMap = new HashMap<>();
        compositions.compositions = compositionsMap;
        
        JsonObject compositionsJson = json.getAsJsonObject();
        for (Map.Entry<String, JsonElement> compositionEntry : compositionsJson.entrySet()) {
            Composition composition = context.deserialize(compositionEntry.getValue(), Composition.class);
            compositionsMap.put(compositionEntry.getKey(), composition);
        }
        
        return compositions;
    }
}

And then register that deserializer on the GsonBuilder as well:然后在 GsonBuilder 上注册该反序列化器:

Gson gson = new GsonBuilder()
    .registerTypeAdapter(ResultInterface.class, new ResultInterfaceDeserializer())
    .registerTypeAdapter(Compositions.class, CompositionsDeserializer.INSTANCE)
    .create();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM