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

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

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

С другой стороны, мы хотим избежать использования объекта-контейнера для хранения наших объектов 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.