1概要

このチュートリアルでは、http://www.smooks.org[Smooks framework]を紹介します。

その内容を説明し、その主な機能を一覧表示して、最終的にそのより高度な機能のいくつかを使用する方法を学習します。

まず最初に、フレームワークが何を達成することを意味しているのか簡単に説明しましょう。


2スモーク

Smooksは、XMLやCSVなどの構造化データを扱うデータ処理アプリケーションのためのフレームワークです。

これは、事前定義されたフォーマット(XMLからCSV、XMLからJSONなど)間の変換を定義できるAPIと構成モデルの両方を提供します。

FreeMarkerやGroovyスクリプトなど、マッピングを設定するためのツールもいくつか使用できます。

変換の他に、Smooksはメッセージ検証やデータ分割のような他の機能も提供します。


2.1. 主な機能

Smooksの主な使用例を見てみましょう。

  • メッセージ変換 – さまざまなソースからのデータの変換

さまざまな出力フォーマットへのフォーマット
** メッセージ強化 – 追加データでメッセージを埋めます。

これはデータベースのような外部データソースから来ます
** データ分割 – 大きなファイル(GB)を処理してそれらをに分割する

小さいもの
** Javaバインディング – メッセージからのJavaオブジェクトの構築と生成

  • メッセージの検証 – 正規表現、あるいはさらには

独自の検証規則を作成する


3初期設定


pom.xml

に追加する必要があるMavenの依存関係から始めましょう。

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

最新版はhttps://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22org.milyn%22%20AND%20a%3A%22milyn-smooks-all%22[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でこのタスクを達成するために、私たちは2つのことをしなければなりません:

POJOとSmooks設定を準備します。

モデルがどのように見えるかを見てみましょう。

public class Order {

    private Date creationDate;
    private Long number;
    private Status status;
   //...
}

public enum Status {
    NEW, IN__PROGRESS, FINISHED
}

それでは、Smooksマッピングに移りましょう。

基本的に、マッピングは変換ロジックを含むXMLファイルです。この記事では、3種類のルールを使用します。


  • bean –

    は、具体的な構造化セクションのJavaへのマッピングを定義します。

クラス
**

value

– Beanの特定のプロパティに対するマッピングを定義します。

マッピングに使用されるデコーダのようなより高度なロジックを含めることができます。
いくつかのデータ型への値(日付や10進数形式など)
** w

__iring –

__を使うと、あるBeanを他のBeanに関連付けることができます(たとえば


Supplier

Beanは

Order

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.baeldung.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 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. 高度なバインディング – 他のBeanやリストを参照する

前の例を

supplier

タグと

order-items

タグで拡張しましょう。

<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;
   //...
}

また、

supplier

および

item

Bean定義を使用して構成マッピングを拡張する必要があります。


ArrayList

のすべての

item

要素を保持する、分離された

items

beanも定義したことに注意してください。

最後に、Smooksの

wiring

属性を使用して、まとめてまとめます。

この場合のマッピングの様子を見てみましょう。

<?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.baeldung.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.baeldung.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.baeldung.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はルールに基づいた検証メカニズムを持っています。それらがどのように使用されているかを見てみましょう。

ルールの定義は、多くの

ruleBase

要素を含むことができる

ruleBases

タグにネストされた設定ファイルに保存されます。



ruleBase

要素には、以下のプロパティが必要です。


  • name –

    一意の名前。参照用です。


  • src –

    ルールのソースファイルへのパス


  • provider

    – 実装されている完全修飾クラス名


RuleProvider

インターフェース

Smooksには、

RegexProvider



MVELProvider

の2つのプロバイダが付属しています。

最初のものは、正規表現のようなスタイルで個々のフィールドを検証するために使用されます。

2番目のものはドキュメントのグローバルな範囲でより複雑な検証を実行するために使用されます。それらを実際に見てみましょう。


5.1.

RegexProvider



RegexProvider

を使用して、顧客名の形式と電話番号の2つを検証しましょう。ソースとしての

RegexProvider

には、Javaプロパティファイルが必要です。このファイルには、キーと値の形式で正規表現の検証を含める必要があります。

Googleの要件を満たすために、次の設定を使用します。

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


5.2.

MVELProvider




order-item

の合計金額が200未満であるかどうかを検証するには、

MVELProvider

を使用します。ソースとして、2つの列(規則名とMVEL式)を含むCSVファイルを用意します。

価格が正しいかどうかを確認するには、次のエントリが必要です。

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


5.3. 検証設定


ruleBases

のソースファイルを準備したら、具体的な検証の実装に進みます。

検証はSmooks設定の中の別のタグで、次の属性が含まれています。


  • executeOn

    – 検証された要素へのパス


  • name



    ruleBase

    への参照


  • 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 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では、このテクニックはtemplating ** とも呼ばれ、以下をサポートします。

  • FreeMarker(推奨オプション)

  • XSL


  • String

    テンプレート

この例では、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 whenApplyEDITemplate__thenConvertedToEDIFACT()
  throws Exception {
    OrderConverter orderConverter = new OrderConverter();
    String edifact = orderConverter.convertOrderXMLtoEDIFACT(
      "/smooks/order.xml");

   assertThat(edifact,is(EDIFACT__MESSAGE));
}


7. 結論

このチュートリアルでは、メッセージをさまざまな形式に変換する方法、またはSmooksを使用してメッセージをJavaオブジェクトに変換する方法に焦点を当てました。また、正規表現やビジネスロジックのルールに基づいて検証を実行する方法も見ました。

いつものように、ここで使われているすべてのコードはhttps://github.com/eugenp/tutorials/tree/master/libraries[over GitHub]にあります。