«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.