Невозможно десериализовать сложные / динамические классы c JSON в Java с помощью Gson - PullRequest
0 голосов
/ 06 августа 2020

Я пытался десериализовать классы с 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.

1 Ответ

0 голосов
/ 29 августа 2020

Вероятно, вы могли бы решить эту проблему, зарегистрировав дополнительный JsonDeserializer для Compositions:

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

А затем зарегистрируйте этот десериализатор в GsonBuilder:

Gson gson = new GsonBuilder()
    .registerTypeAdapter(ResultInterface.class, new ResultInterfaceDeserializer())
    .registerTypeAdapter(Compositions.class, CompositionsDeserializer.INSTANCE)
    .create();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...