Я пытался десериализовать классы с JSON до Java с помощью Gson, но структура JSON слишком сложна для меня. 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"
}
Основной объект JSON имеет три поля: results
, pagination
и timing
. Я могу десериализовать pagination
и timing
отлично, поскольку они всегда имеют одинаковую структуру. Я не могу правильно десериализовать results
.
results
всегда представляет собой список из двух разных объектов. В частности, второй объект является наиболее сложным, так как его имена полей не являются статическими c. Ссылки на имена UUID всегда меняются при каждом ответе API. Например, поле с именем "163eee06-83a4-4fd8-bf65-5d6a3ef35ac5"
может иметь другой идентификатор в следующем ответе JSON. Поэтому я не могу дать ему правильное имя поля в соответствующем классе Java. То же самое касается "d5760d01-84dd-42b2-8001-a69ebaa4c2df"
и "fb366b72-d567-4d23-9f5f-356fc09aff6f"
в этом случае.
Есть идеи, как правильно десериализовать этот тип JSON с помощью Gson? Я пробовал несколько разных подходов, но пока ничего по-настоящему не сработало.
В самой последней попытке я пытался использовать подход JsonDeserializer, чтобы различать типы объектов в списке results
. Моя текущая реализация выглядит так (геттеры и сеттеры были скрыты из-за пробела):
QueryResponse. java
public class QueryResponse {
private List<Map<String, ResultInterface>> results;
private Pagination pagination;
private String timing;
}
Pagination. java
public class Pagination {
private Integer max;
private Integer offset;
private Integer nextOffset;
private Integer previousOffset;
}
ResultInterface. java
public interface ResultInterface {
}
ElementDefinition. java
public class ElementDefinition implements ResultInterface {
private String type;
private Name name;
private List<String> attrs;
}
Имя. java
public class Name {
private String en;
private String es;
}
Составы. java
public class Compositions implements ResultInterface {
private Map<String, Composition> compositions;
}
Состав. java
public class Composition {
private String date;
private List<Col> cols;
}
Кол. java
public class Col {
private String type;
private String path;
private List<Value> values;
}
Значение. 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
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);
}
}
}
Я вызываю Gson так:
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(ResultInterface.class, new ResultInterfaceDeserializer());
Gson gson = builder.create();
QueryResponse queryResponse = gson.fromJson(externalJsonResponse, QueryResponse.class);
Проблема с этой реализацией в том, что в структуре JSON нет ничего с именем compositions
, поэтому класс Compositions.java
определен неправильно. Я знаю, что мне нужно использовать структуры Java, такие как Map<String, SomeObject>
, но проблема в том, что здесь слишком много полей с динамически именованными Json, и я не могу «захватить» их, если у них нет фиксированного идентификатора имени.
ОБНОВЛЕНИЕ
Мне удалось найти решение. Я бы сказал, что на самом деле это обходной путь и, вероятно, не самое чистое или элегантное решение. Проблема с моей текущей реализацией заключалась в том, что я пытался «захватить» поле JSON с именем compositions
, хотя на самом деле его не существовало. Итак, я решил манипулировать JSON и сам добавить это поле (в код). Я изменил класс десериализатора на:
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);
}
}
}
С этим изменением я теперь могу «захватить» поле композиций и получить данные в Java POJO.