こんにちは、キャスレーコンサルティングのSD(システム・デザイン)部ヤマナです。

今回はjOOXというjavaのライブラリを紹介させて頂きます。
jQueryライクなAPIでXMLの読取り/書込みが可能です。

今回は以下の操作についてjOOXのサンプルソースをご紹介します。

  • ファイル読取
  • Webからの読取
  • ゼロから作成
  • 既存XMLの変更

ファイル読取

まずは読取りの例から。

以下の様なファイルを対象とする場合。

<?xml version="1.0"?>
<parent>
  <children>
    <child id="elder"><age>10</age><name>taro</name><gender>male</gender></child>
    <child id="younger"><age>6</age><name>jiro</name><gender>male</gender></child>
  </children>
</parent>

以下の様に、jQueryのセレクタと同様コードでIDによる抽出が可能です。

import static org.joox.JOOX.$;
import org.joox.Match;

Match fromFile = $(new File("sample.xml"));
Match younger = fromFile.find("#younger");
System.out.println( younger.child("name").text() );  // ===> jiro
System.out.println( younger.child("age").text() );   // ===> 6

Matchオブジェクトは、jQueryオブジェクトのようなものです。
内部的にxmlの要素(のリスト)を保持しています。
jQeuryオブジェクトと同様に、textメソッドによる子要素の入出力やfind,filter,childメソッドによる更なる絞り込みが可能です。

Webからの読取

また、取得元としてURLを指定することも可能です。
maven centralのpomを対象として、dependencyをgradleの形式に変換する例がこちらです。

import java.net.URL;

Match pom =
  $(new URL("http://central.maven.org/maven2/org/springframework/spring-core/4.2.5.RELEASE/spring-core-4.2.5.RELEASE.pom"));
List<String> dependencies = pom.find("dependency").each().stream()
  .map( d ->
    d.find("groupId").text() + ":" +
    d.find("artifactId").text() + ":" +
    d.find("version").text())
  .collect(Collectors.toList());
System.out.println(dependencies);

MatchオブジェクトはjQueryオブジェクト同様に、内部的に要素のリストを保持しています。
これを取り出すのがMatch#eachメソッドで、上記の例ではその結果からさらにjava8のstreamメソッドを呼び出してリストへの変換を行っています。

結果は以下のようになります。

[commons-codec:commons-codec:1.10, commons-logging:commons-logging:1.2, log4j:log4j:1.2.17, net.sf.jopt-simple:jopt-simple:4.9, org.aspectj:aspectjweaver:1.8.8]

さて、次はXMLを作成する例です。

ゼロから作成

import static org.joox.JOOX.$;
import org.joox.Match;

Match parent1 =
  $("parent").append(
    $("child").append($("number").text("8.1"))
              .append($("string").text("ON"))
  );
System.out.println(parent);

出力結果(XMLは整形しています)

<parent>
  <child>
    <number>8.1</number>
    <string>ON</string>
  </child>
</parent>

これだけでも十分スッキリとかけていますが、さらに短縮が可能です。

まず、上記の例では以下の様な処理を行っています。

  • “$”メソッドにタグ名を渡す事で要素”parent”, “child”のインスタンスを生成
  • number, string要素のインスタンスのtextメソッドでテキストノードを追加(textメソッドはインスタンスをそのまま返します)
  • 要素”child”にnumber, string要素を追加
  • parent要素にそのchild要素を追加

“$”メソッドには以下のオーバーロードがあるため、

$(String name, String content)

こう書けます。

$("parent").append(
  $("child").append(
    $("number", "8.1"),
    $("string", "ON")
  ))

さらに、以下のオーバーロードも存在するため、

$(String name, Match ... content)

こう書けます。

$("parent",
  $("child",
    $("number", "8.1"),
    $("string", "ON")
))

非常にスッキリとしました。

既存XMLの変更

既存XMLは以下のものとします。

<?xml version="1.0"?>
<parent>
  <children>
    <child id="elder"><age>10</age><name>taro</name><gender>male</gender></child>
    <child id="younger"><age>6</age><name>jiro</name><gender>male</gender></child>
  </children>
</parent>

コードは以下の通り。

Match fromFile = $(new File("sample.xml"));

// $メソッドでPOJOをXML化も可能
fromFile.find("children").append( $(new Child("hanako", 1, "female")).attr("id", "sister") );

// gender要素のテキストが'male'のみの'child'要素を抽出
Match bros = fromFile.find("child").filter( c -> $(c).child("gender").text().equals("male") );
bros.each().stream().forEach( b -> b.find("age").text( String.valueOf(Integer.valueOf(b.find("age").text()) + 2)) );

System.out.println(fromFile);

出力結果(XMLは整形しています)

<parent>
  <children>
    <child id="elder"><age>12</age><name>taro</name><gender>male</gender></child>
    <child id="younger"><age>8</age><name>jiro</name><gender>male</gender></child>
    <child id="sister"><age>1</age><gender>female</gender><name>hanako</name></child>
  </children>
</parent>

上記の例では、前述のXMLファイルに対し、以下の様な操作を行っています。

  • name=”hanako”, age=1, gender=”female”というフィールドを持つChildオブジェクトを$メソッドによりElementに変換し、childerenに追加
    (この時のタグ名は、クラス名を小文字にしたものになるようです)
  • 追加後のchild要素のうち、gender=”male”という子要素を持つ物を抽出
  • 上記抽出結果の全てに対し、age=age+2を設定

複雑な条件の抽出の場合、filterメソッドやfindメソッドにorg.joox.Filterインターフェースを実装したオブジェクトを渡す必要があるのですが、
booleanを返すメソッドを1つだけ持つインターフェースのため、java8との相性も良いのではないかと思います。

さいごに

先日実務にて、XMLを入力として受け取るような帳票エンジンを使用した際にjOOXを使用したため、簡単にご紹介させていただきました。

JSONに押され、以前に比べると使用頻度の下がったXMLですが、たまに使うこともあると思います。
java標準APIでは要素の取得にxpathを使う必要があるなど、苦しい部分もjOOXで緩和出来る場面もあるのではないでしょうか。

なお、今回のサンプルコードおよびgradleのファイルは以下に共有しています。
https://github.com/yohei224/joox_example

宜しければご覧になってみてください。