Java распарсить JSON с глубокой вложенностью

204
16 апреля 2019, 08:30

Нужно распарсить такой сложный JSON:

{
  "receiveData": {
    "iBusData": {
      "requestUID": "44bd8a65-d128-48a4-b070-5f90fc5a7674",
      "messageKind": "PublishedData",
      "sender": "ST-DELLIN-307",
      "senderDateTime": "2018-11-07T10:43:41",
      "typeName": "typeInvoice",
      "currentSenderApplication": "GP",
      "objectUID": "322dd3cf-ae4c-4fcc-b785-c2b7698b85b4",
      "data": {
        "invoice": {
          "uid": "322dd3cf-ae4c-4fcc-b785-c2b7698b85b4",
          "presentation": "Test_Invoice",
          "date": "2018-11-07T10:43:42",
          "number": "Test_Invoice",
          "marked": false,
          "posted": false,
          "senderCityUID": "d645a316-564b-4f38-b0b6-4a1541eb70ce",
          "receiverTerminalUID": "d8bd5e84-c604-4125-aa2b-55a6f23f9e85",
          "receiverCityUID": "e94042e3-d88e-4097-9349-878989ad28c1",
          "cargoUID": "21e8eb95-b0aa-477c-b756-e7304921a3d2",
          "amount": 1,
          "amountExtraLarge": 1,
          "netWeight": 1,
          "netWeightExtraLarge": 1,
          "grossWeight": 1,
          "netVolume": 1,
          "netVolumeExtraLarge": 1,
          "grossVolume": 1,
          "declaredValue": 1,
          "contractorSenderUID": "e5a4a6ff-2a74-46ac-9e98-7a6cab5dc388",
          "contractorReceiverUID": "993fb6ed-70cc-4b47-87b6-7e0a82affdb2",
          "contractorPayerUID": "7b60e946-d327-44d4-a0a5-2ebf5ef1ad1c",
          "contractorSenderIssueUID": "ef0453ff-c8df-4fd1-95ab-7943baee9c71",
          "contractorReceiverIssueUID": "aaf30c1d-8d8b-4cf9-a1a1-04d8b3d0eab3",
          "actualReceiver": "Test_Invoice",
          "freightInKops": 1,
          "deliveryTimeInsuranceSum": 1,
          "terminalUID": "47e1481c-1191-49ba-ad11-aceb46284fca",
          "cargoInsuranceSum": 1,
          "personalID": null,
          "senderCityName": "Test_Invoice",
          "senderCityKLADR": "Test_Invoice",
          "receiverCityName": "Test_Invoice",
          "receiverCityKLADR": "Test_Invoice",
          "transportationCostOnReceiving": 1,
          "transportationCostOnIssuing": 1,
          "invoiceUID": "35faff4b-85e3-428b-ab09-4462f118a585"
        }
      }
    }
  }
}

Сложность в том, что эти данные надо обработать так:

  • Отобразить JSON с вложенностью на объект без вложенности, содержащий те же самые поля
  • Взять хэш от вложенного объекта data и записать его в поле объекта Java.

Как это можно сделать при помощи библиотек десериализации типа Jackson или Gson? В интернете много примеров того, как вложенный json преобразуется в объект с такой же вложенностью. Но мне нужны более сложные операции.

Answer 1

Воссоздаем аналогичную структуру JSON в POJO классе, и уже парсим респонс так

MyPojo string = new Gson().fromJson(jsonStr, MyPojo.class);

Преобразование POJO класса обратно

String jsonStr = new Gson().toJson(myPojo);

По "не вложенности" - как вариант, создать доп. класс с функцией конвертирования полученного объекта в новый без вложенности и обратно (mapstruct в этом поможет), а там уже и хеши и что уже захотите с ним сделать.

Answer 2

(Jackson) Вам может помочь аннотация @JsonDeserialize(using = DataDeserializer.class), где DataDeserializer это десериализатор, в котором можно прописать любую логику конвертации. Как пример создаём генерик класс, который лазит по всем классам и собирает самые нижние поля.

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.time.LocalDateTime;
import java.util.Iterator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
public class CustomDeserializer<T> extends StdDeserializer<T> {
    protected CustomDeserializer() {
        super(LocalDateTime.class);
    }
    @Override
    public T deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
            throws IOException, JsonProcessingException {
        try {
            final T instance = ((Class<
                    T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0])
                        .newInstance();
            final TreeNode treeNode = jsonParser.readValueAsTree();
            parseNode(treeNode, instance);
            return instance;
        } catch (NoSuchFieldException | IllegalAccessException | InstantiationException e) {
            throw new RuntimeException(e);
        }
    }
    private void parseNode(final TreeNode treeNode, final T instance)
            throws IOException, NoSuchFieldException, IllegalAccessException {
        final Iterator<String> names = treeNode.fieldNames();
        while (names.hasNext()) {
            final String nodeName = names.next();
            final TreeNode nextNode = treeNode.get(nodeName);
            if (nextNode.isObject()) {
                parseNode(nextNode, instance);
            } else {
                final String value = nextNode.toString();
                final Field declaredField = Data.class.getDeclaredField(nodeName);
                final Class<?> fieldType = declaredField.getType();
                final ObjectMapper objectMapper = new ObjectMapper();
                final Object deserializedValue = objectMapper.readValue(value, fieldType);
                declaredField.setAccessible(true);
                declaredField.set(instance, deserializedValue);
            }
        }
    }
}

Дальше создаём наследника c генериком нужного нам типа:

public class DataDeserializer extends CustomDeserializer<Data> {}

Затем помечаем наш класс аннотацией:

@JsonDeserialize(using = DataDeserializer.class)
public class Data {...

Ну и парсим:

final ObjectMapper objectMapper = new ObjectMapper();
try {
    final Data data = objectMapper.readValue("JSON_VALUE", Data.class);
} catch (IOException e) {
    ...
}

C простыми полями должно сработать. Теоретически, можно отфильтровать объекты, поля которых забирать наверх не нужно.

READ ALSO
Как перемесить изображение?

Как перемесить изображение?

Нужно,что бы при выборе изображения оно перемещалось в корневую папку программы, как это можно реализовать ? Пытался через Filescopy и Files

180
Абстрактный класс или интерфейс?

Абстрактный класс или интерфейс?

У меня имееться два подобных класса LowPathFilter и HighPathFilter

169
Как сделать запрос на api сервер через java

Как сделать запрос на api сервер через java

Необходимо получить информацию о курсе валют , на сайте банка есть api в формате json нужно как получить себе в приложениеhttp://www

180
Пример из Spring4. Spring+Hibernate. org.springframework.beans.factory.BeanCreationException: Error creating bean with name &#39;transactionManager&#39;

Пример из Spring4. Spring+Hibernate. org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'transactionManager'

Реализую пример из книги "Spring 4 для профессионалов" из главы "Использование Hibernate в Spring"Использую БД H2

185