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

Введение в Смукс

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

1. Обзор

В этом уроке мы познакомимся с фреймворком Smooks .

Мы опишем, что это такое, перечислим его ключевые функции и, в конце концов, узнаем, как использовать некоторые из его более продвинутых функций.

Прежде всего, давайте кратко объясним, для чего предназначен фреймворк.

2. Курит

Smooks — это платформа для приложений обработки данных, которая работает со структурированными данными, такими как XML или CSV.

Он предоставляет как API, так и модель конфигурации, которые позволяют нам определять преобразования между предопределенными форматами (например, XML в CSV, XML в JSON и т. д.).

Мы также можем использовать ряд инструментов для настройки нашего сопоставления, включая скрипты FreeMarker или Groovy.

Помимо преобразований, Smooks также предоставляет другие функции, такие как проверка сообщений или разделение данных.

2.1. Ключевая особенность

Давайте рассмотрим основные варианты использования Smooks:

  • Преобразование сообщений - преобразование данных из различных исходных форматов в различные выходные форматы.
  • Обогащение сообщения — заполнение сообщения дополнительными данными, которые поступают из внешнего источника данных, такого как база данных.
  • Разделение данных — обработка больших файлов (ГБ) и их разделение на более мелкие
  • Привязка Java - создание и заполнение объектов Java из сообщений.
  • Проверка сообщений — выполнение проверок, таких как регулярное выражение, или даже создание собственных правил проверки.

3. Начальная конфигурация

Давайте начнем с зависимости Maven, которую нам нужно добавить в наш pom.xml :

<dependency>
<groupId>org.milyn</groupId>
<artifactId>milyn-smooks-all</artifactId>
<version>1.7.0</version>
</dependency>

Последнюю версию можно найти на Maven Central .

4. Привязка Java

Теперь давайте начнем с привязки сообщений к классам Java. Здесь мы рассмотрим простое преобразование XML в Java.

4.1. Основные понятия

Мы начнем с простого примера. Рассмотрим следующий XML:

<order creation-date="2018-01-14">
<order-number>771</order-number>
<order-status>IN_PROGRESS</order-status>
</order>

Чтобы выполнить эту задачу с помощью Smooks, нам нужно сделать две вещи: подготовить POJO и настроить Smooks.

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

public class Order {

private Date creationDate;
private Long number;
private Status status;
// ...
}
public enum Status {
NEW, IN_PROGRESS, FINISHED
}

Теперь давайте перейдем к отображениям Smooks.

По сути, сопоставления представляют собой XML-файл, содержащий логику преобразования. В этой статье мы будем использовать три разных типа правил:

  • bean — определяет сопоставление конкретного структурированного раздела с классом Java.
  • value — определяет отображение для конкретного свойства bean-компонента. Может содержать более сложную логику, например декодеры, которые используются для сопоставления значений с некоторыми типами данных (например, датой или десятичным форматом).
  • проводка — позволяет нам связать бин с другими бинами (например , бин Поставщик будет связан с бином Заказ )

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

<?xml version="1.0"?>
<smooks-resource-list
xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"
xmlns:jb="http://www.milyn.org/xsd/smooks/javabean-1.2.xsd">

<jb:bean beanId="order"
class="com.foreach.smooks.model.Order" createOnElement="order">
<jb:value property="number" data="order/order-number" />
<jb:value property="status" data="order/order-status" />
<jb:value property="creationDate"
data="order/@creation-date" decoder="Date">
<jb:decodeParam name="format">yyyy-MM-dd</jb:decodeParam>
</jb:value>
</jb:bean>
</smooks-resource-list>

Теперь, когда конфигурация готова, давайте попробуем проверить, правильно ли построен наш POJO.

Во-первых, нам нужно создать объект Smooks и передать входной XML в виде потока:

public Order converOrderXMLToOrderObject(String path) 
throws IOException, SAXException {

Smooks smooks = new Smooks(
this.class.getResourceAsStream("/smooks-mapping.xml"));
try {
JavaResult javaResult = new JavaResult();
smooks.filterSource(new StreamSource(this.class
.getResourceAsStream(path)), javaResult);
return (Order) javaResult.getBean("order");
} finally {
smooks.close();
}
}

И, наконец, проверьте правильность настройки:

@Test
public void givenOrderXML_whenConvert_thenPOJOsConstructedCorrectly() throws Exception {
XMLToJavaConverter xmlToJavaOrderConverter = new XMLToJavaConverter();
Order order = xmlToJavaOrderConverter
.converOrderXMLToOrderObject("/order.xml");

assertThat(order.getNumber(), is(771L));
assertThat(order.getStatus(), is(Status.IN_PROGRESS));
assertThat(
order.getCreationDate(),
is(new SimpleDateFormat("yyyy-MM-dd").parse("2018-01-14"));
}

4.2. Advanced Binding — ссылки на другие компоненты и списки

Давайте расширим наш предыдущий пример с тегами поставщика и заказа :

<order creation-date="2018-01-14">
<order-number>771</order-number>
<order-status>IN_PROGRESS</order-status>
<supplier>
<name>Company X</name>
<phone>1234567</phone>
</supplier>
<order-items>
<item>
<quanitiy>1</quanitiy>
<code>PX1234</code>
<price>9.99</price>
</item>
<item>
<quanitiy>1</quanitiy>
<code>RX990</code>
<price>120.32</price>
</item>
</order-items>
</order>

А теперь давайте обновим нашу модель:

public class Order {
// ..
private Supplier supplier;
private List<Item> items;
// ...
}
public class Item {

private String code;
private Double price;
private Integer quantity;
// ...
}
public class Supplier {

private String name;
private String phoneNumber;
// ...
}

Мы также должны расширить отображение конфигурации с помощью определений bean -компонентов поставщика и элемента данных.

Обратите внимание, что мы также определили bean-компонент с отдельными элементами , который будет содержать все элементы элементов в ArrayList .

Наконец, мы будем использовать атрибут проводки Smooks , чтобы связать все это вместе.

Посмотрите, как будут выглядеть сопоставления в этом случае:

<?xml version="1.0"?>
<smooks-resource-list
xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"
xmlns:jb="http://www.milyn.org/xsd/smooks/javabean-1.2.xsd">

<jb:bean beanId="order"
class="com.foreach.smooks.model.Order" createOnElement="order">
<jb:value property="number" data="order/order-number" />
<jb:value property="status" data="order/order-status" />
<jb:value property="creationDate"
data="order/@creation-date" decoder="Date">
<jb:decodeParam name="format">yyyy-MM-dd</jb:decodeParam>
</jb:value>
<jb:wiring property="supplier" beanIdRef="supplier" />
<jb:wiring property="items" beanIdRef="items" />
</jb:bean>

<jb:bean beanId="supplier"
class="com.foreach.smooks.model.Supplier" createOnElement="supplier">
<jb:value property="name" data="name" />
<jb:value property="phoneNumber" data="phone" />
</jb:bean>

<jb:bean beanId="items"
class="java.util.ArrayList" createOnElement="order">
<jb:wiring beanIdRef="item" />
</jb:bean>
<jb:bean beanId="item"
class="com.foreach.smooks.model.Item" createOnElement="item">
<jb:value property="code" data="item/code" />
<jb:value property="price" decoder="Double" data="item/price" />
<jb:value property="quantity" decoder="Integer" data="item/quantity" />
</jb:bean>

</smooks-resource-list>

Наконец, мы добавим несколько утверждений к нашему предыдущему тесту:

assertThat(
order.getSupplier(),
is(new Supplier("Company X", "1234567")));
assertThat(order.getItems(), containsInAnyOrder(
new Item("PX1234", 9.99,1),
new Item("RX990", 120.32,1)));

5. Проверка сообщений

Smooks поставляется с механизмом проверки, основанным на правилах. Давайте посмотрим, как они используются.

Определение правил хранится в конфигурационном файле, вложенном в тег ruleBases , который может содержать множество элементов ruleBase .

Каждый элемент ruleBase должен иметь следующие свойства:

  • name – уникальное имя, используемое только для справки
  • src – путь к исходному файлу правила
  • provider — полное имя класса, который реализует интерфейс RuleProvider.

Smooks поставляется с двумя провайдерами из коробки: RegexProvider и MVELProvider .

Первый используется для проверки отдельных полей в стиле регулярных выражений.

Второй используется для выполнения более сложной проверки в глобальной области документа. Давайте посмотрим на них в действии.

5.1. RegexProvider

Давайте используем RegexProvider для проверки двух вещей: формата имени клиента и номера телефона. RegexProvider в качестве источника требуется файл свойств Java, который должен содержать проверку регулярных выражений в виде ключ-значение .

Чтобы удовлетворить наши требования, мы будем использовать следующую настройку:

supplierName=[A-Za-z0-9]*
supplierPhone=^[0-9\\-\\+]{9,15}$

5.2. МВЕЛПровайдер

Мы будем использовать MVELProvider , чтобы проверить, меньше ли общая цена каждого элемента заказа 200. В качестве источника мы подготовим файл CSV с двумя столбцами: имя правила и выражение MVEL.

Чтобы проверить правильность цены, нам нужна следующая запись:

"max_total","orderItem.quantity * orderItem.price < 200.00"

5.3. Конфигурация проверки

После того, как мы подготовили исходные файлы для правил , мы перейдем к реализации конкретных проверок.

Валидация — это еще один тег в конфигурации Smooks, который содержит следующие атрибуты:

  • executeOn — путь к проверенному элементу
  • name – ссылка на базу правил
  • onFail — указывает, какие действия будут предприняты, если проверка не пройдена.

Давайте применим правила проверки к нашему файлу конфигурации Smooks и проверим, как он выглядит ( обратите внимание, что если мы хотим использовать MVELProvider , мы вынуждены использовать привязку Java, поэтому мы импортировали предыдущую конфигурацию Smooks):

<?xml version="1.0"?>
<smooks-resource-list
xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"
xmlns:rules="http://www.milyn.org/xsd/smooks/rules-1.0.xsd"
xmlns:validation="http://www.milyn.org/xsd/smooks/validation-1.0.xsd">

<import file="smooks-mapping.xml" />

<rules:ruleBases>
<rules:ruleBase
name="supplierValidation"
src="supplier.properties"
provider="org.milyn.rules.regex.RegexProvider"/>
<rules:ruleBase
name="itemsValidation"
src="item-rules.csv"
provider="org.milyn.rules.mvel.MVELProvider"/>
</rules:ruleBases>

<validation:rule
executeOn="supplier/name"
name="supplierValidation.supplierName" onFail="ERROR"/>
<validation:rule
executeOn="supplier/phone"
name="supplierValidation.supplierPhone" onFail="ERROR"/>
<validation:rule
executeOn="order-items/item"
name="itemsValidation.max_total" onFail="ERROR"/>

</smooks-resource-list>

Теперь, когда конфигурация готова, давайте попробуем проверить, не пройдет ли проверка номера телефона поставщика.

Опять же, мы должны построить объект Smooks и передать входной XML в виде потока:

public ValidationResult validate(String path) 
throws IOException, SAXException {
Smooks smooks = new Smooks(OrderValidator.class
.getResourceAsStream("/smooks/smooks-validation.xml"));
try {
StringResult xmlResult = new StringResult();
JavaResult javaResult = new JavaResult();
ValidationResult validationResult = new ValidationResult();
smooks.filterSource(new StreamSource(OrderValidator.class
.getResourceAsStream(path)), xmlResult, javaResult, validationResult);
return validationResult;
} finally {
smooks.close();
}
}

И, наконец, подтвердите, если произошла ошибка проверки:

@Test
public void givenIncorrectOrderXML_whenValidate_thenExpectValidationErrors() throws Exception {
OrderValidator orderValidator = new OrderValidator();
ValidationResult validationResult = orderValidator
.validate("/smooks/order.xml");

assertThat(validationResult.getErrors(), hasSize(1));
assertThat(
validationResult.getErrors().get(0).getFailRuleResult().getRuleName(),
is("supplierPhone"));
}

6. Преобразование сообщений

Следующее, что мы хотим сделать, это преобразовать сообщение из одного формата в другой.

В Smooks этот метод также называется шаблонированием, и он поддерживает:

  • FreeMarker (предпочтительный вариант)
  • XSL
  • `Шаблон строки`

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

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

UNA:+.? '
UNH+${order.number}+${order.status}+${order.creationDate?date}'
CTA+${supplier.name}+${supplier.phoneNumber}'
<#list items as item>
LIN+${item.quantity}+${item.code}+${item.price}'
</#list>

И для сообщения электронной почты:

Hi,
Order number #${order.number} created on ${order.creationDate?date} is currently in ${order.status} status.
Consider contacting the supplier "${supplier.name}" with phone number: "${supplier.phoneNumber}".
Order items:
<#list items as item>
${item.quantity} X ${item.code} (total price ${item.price * item.quantity})
</#list>

На этот раз конфигурация Smooks очень проста (просто не забудьте импортировать предыдущую конфигурацию, чтобы импортировать настройки привязки Java):

<?xml version="1.0"?>
<smooks-resource-list
xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"
xmlns:ftl="http://www.milyn.org/xsd/smooks/freemarker-1.1.xsd">

<import file="smooks-validation.xml" />

<ftl:freemarker applyOnElement="#document">
<ftl:template>/path/to/template.ftl</ftl:template>
</ftl:freemarker>

</smooks-resource-list>

На этот раз нам нужно просто передать StringResult движку Smooks:

Smooks smooks = new Smooks(config);
StringResult stringResult = new StringResult();
smooks.filterSource(new StreamSource(OrderConverter.class
.getResourceAsStream(path)), stringResult);
return stringResult.toString();

И мы можем, конечно, проверить это:

@Test
public void givenOrderXML_whenApplyEDITemplate_thenConvertedToEDIFACT()
throws Exception {
OrderConverter orderConverter = new OrderConverter();
String edifact = orderConverter.convertOrderXMLtoEDIFACT(
"/smooks/order.xml");

assertThat(edifact,is(EDIFACT_MESSAGE));
}

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

В этом руководстве мы сосредоточились на том, как преобразовывать сообщения в различные форматы или преобразовывать их в объекты Java с помощью Smooks. Мы также увидели, как выполнять проверки на основе регулярных выражений или правил бизнес-логики.

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