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 .