Перейти к основному содержимому

Разупорядочение массива JSON с использованием camel-jackson

· 5 мин. чтения

1. Обзор

Apache Camel — это мощная среда интеграции с открытым исходным кодом, реализующая ряд известных шаблонов интеграции предприятия .

Обычно при работе с маршрутизацией сообщений с помощью Camel мы хотим использовать один из многих поддерживаемых подключаемых форматов данных . Учитывая, что JSON популярен в большинстве современных API и служб данных, выбор становится очевидным.

В этом руководстве мы рассмотрим несколько способов, с помощью которых можно преобразовать массив JSON в список объектов Java с помощью компонента camel-jackson .

2. Зависимости

Во-первых, давайте добавим зависимость camel- jackson к нашему pom.xml :

<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-jackson</artifactId>
<version>3.6.0</version>
</dependency>

Затем мы также добавим зависимость camel-test специально для наших модульных тестов, которая также доступна в Maven Central :

<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-test</artifactId>
<version>3.6.0</version>
</dependency>

3. Классы фруктовых доменов

В этом уроке мы будем использовать пару легких объектов POJO для моделирования нашего фруктового домена.

Давайте продолжим и определим класс с идентификатором и именем для представления фруктов:

public class Fruit {

private String name;
private int id;

// standard getter and setters
}

Далее мы определим контейнер для хранения списка объектов Fruit :

public class FruitList {

private List<Fruit> fruits;

public List<Fruit> getFruits() {
return fruits;
}

public void setFruits(List<Fruit> fruits) {
this.fruits = fruits;
}
}

В следующих нескольких разделах мы увидим, как разобрать строку JSON, представляющую список фруктов, в эти классы предметной области. В конечном итоге нам нужна переменная типа List<Fruit> , с которой мы можем работать .

4. Разупорядочение JSON FruitList

В этом первом примере мы собираемся представить простой список фруктов в формате JSON:

{
"fruits": [
{
"id": 100,
"name": "Banana"
},
{
"id": 101,
"name": "Apple"
}
]
}

Прежде всего, мы должны подчеркнуть, что этот JSON представляет объект, который содержит свойство с именем фрукты, которое содержит наш массив .

Теперь давайте настроим наш маршрут Apache Camel для выполнения десериализации:

@Override
protected RouteBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
@Override
public void configure() throws Exception {
from("direct:jsonInput")
.unmarshal(new JacksonDataFormat(FruitList.class))
.to("mock:marshalledObject");
}
};
}

В этом примере мы используем прямую конечную точку с именем jsonInput . Затем мы вызываем метод unmarshal , который распаковывает тело сообщения на нашем обмене Camel, используя указанный формат данных.

Мы используем класс JacksonDataFormat с нестандартным типом FruitList . По сути, это простая оболочка вокруг ObjectMapper от Jackon , которая позволяет нам маршалировать в JSON и из него.

Наконец, мы отправляем результат метода unmarshal в фиктивную конечную точку с именем marshalledObject . Как мы увидим, именно так мы проверим наш маршрут, чтобы убедиться, что он работает правильно.

Имея это в виду, давайте напишем наш первый модульный тест:

public class FruitListJacksonUnmarshalUnitTest extends CamelTestSupport {

@Test
public void givenJsonFruitList_whenUnmarshalled_thenSuccess() throws Exception {
MockEndpoint mock = getMockEndpoint("mock:marshalledObject");
mock.expectedMessageCount(1);
mock.message(0).body().isInstanceOf(FruitList.class);

String json = readJsonFromFile("/json/fruit-list.json");
template.sendBody("direct:jsonInput", json);
assertMockEndpointsSatisfied();

FruitList fruitList = mock.getReceivedExchanges().get(0).getIn().getBody(FruitList.class);
assertNotNull("Fruit lists should not be null", fruitList);

List<Fruit> fruits = fruitList.getFruits();
assertEquals("There should be two fruits", 2, fruits.size());

Fruit fruit = fruits.get(0);
assertEquals("Fruit name", "Banana", fruit.getName());
assertEquals("Fruit id", 100, fruit.getId());

fruit = fruits.get(1);
assertEquals("Fruit name", "Apple", fruit.getName());
assertEquals("Fruit id", 101, fruit.getId());
}
}

Давайте пройдемся по ключевым частям нашего теста, чтобы понять, что происходит:

  • Прежде всего, мы начнем с расширения класса CamelTestSupport — полезного базового класса утилиты тестирования.
  • Затем мы устанавливаем наши тестовые ожидания. Наша фиктивная переменная должна иметь одно сообщение, а тип сообщения должен быть FruitList.
  • Теперь мы готовы отправить входной файл JSON в виде строки в прямую конечную точку, которую мы определили ранее .
  • После того, как мы проверим, что наши фиктивные ожидания были удовлетворены, мы можем получить FruitList и проверить, что содержимое соответствует ожидаемому.

Этот тест подтверждает, что наш маршрут работает правильно, и наш JSON не сортируется, как и ожидалось. Потрясающий!

5. Разупорядочение фруктового массива JSON

С другой стороны, мы хотим избежать использования объекта-контейнера для хранения наших объектов Fruit . Мы можем изменить наш JSON, чтобы он содержал массив фруктов напрямую:

[
{
"id": 100,
"name": "Banana"
},
{
"id": 101,
"name": "Apple"
}
]

На этот раз наш маршрут почти идентичен, но мы настроили его специально для работы с массивом JSON:

@Override
protected RouteBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
@Override
public void configure() throws Exception {
from("direct:jsonInput")
.unmarshal(new ListJacksonDataFormat(Fruit.class))
.to("mock:marshalledObject");
}
};
}

Как мы видим, единственное отличие от нашего предыдущего примера заключается в том, что мы используем класс ListJacksonDataFormat с пользовательским немаршальным типом Fruit . Это тип формата данных Джексона, подготовленный непосредственно для работы со списками .

Точно так же наш модульный тест очень похож:

@Test
public void givenJsonFruitArray_whenUnmarshalled_thenSuccess() throws Exception {
MockEndpoint mock = getMockEndpoint("mock:marshalledObject");
mock.expectedMessageCount(1);
mock.message(0).body().isInstanceOf(List.class);

String json = readJsonFromFile("/json/fruit-array.json");
template.sendBody("direct:jsonInput", json);
assertMockEndpointsSatisfied();

@SuppressWarnings("unchecked")
List<Fruit> fruitList = mock.getReceivedExchanges().get(0).getIn().getBody(List.class);
assertNotNull("Fruit lists should not be null", fruitList);

// more standard assertions
}

Однако есть два тонких отличия от теста, который мы видели в предыдущем разделе:

  • Сначала мы настраиваем наше фиктивное ожидание, чтобы оно содержало тело непосредственно с List.class.
  • Когда мы получаем тело сообщения как List.class , мы получаем стандартное предупреждение о безопасности типов — отсюда и использование @SuppressWarnings(“unchecked”)

6. Заключение

В этой короткой статье мы рассмотрели два простых подхода к разупорядочению массивов JSON с использованием маршрутизации сообщений верблюда и компонента верблюда-джексона .

Как всегда, полный исходный код статьи доступен на GitHub .