XStreamを使ってXML-Object変換のUtilクラスを作ってみた

XStreamは、XMLJavaオブジェクトのマッピングができます。

XMLファイルからシリアライズ、デシリアライズもできますが、
今回は、文字列としてのXMLとオブジェクトの変換してるだけです。

package org.ryu1.utils;

import com.thoughtworks.xstream.XStream;

/**
 * XMLUtilクラス.
 */
public final class XmlUtil {

    private XmlUtil() {
    }

    /**
     * XML文字列をオブジェクトに変換します
     * @param clazz
     * @param xml
     * @return object of clazz
     */
    @SuppressWarnings("unchecked")
    public static <T> T fromXML(Class<T> clazz, String xml) {
        XStream xstream = new XStream();
        xstream.processAnnotations(clazz);
        return (T) xstream.fromXML(xml);
    }

    /**
     * オブジェクトをXML文字列に変換します
     * @param obj
     * @return String of XML
     */
    public static <T> String toXML(T obj) {
        XStream xstream = new XStream();
        xstream.processAnnotations(obj.getClass());
        return xstream.toXML(obj);
    }

}

ちょっぴり、背伸びして、Genericを使い、キャストの手間を省いてみました。
fromXMLだけ、クラスを渡さないといけないのが、かっこわるいです。

使い方のサンプルはこちら。

package org.ryu1.utils;

import com.thoughtworks.xstream.annotations.XStreamAlias;

import java.util.ArrayList;
import java.util.List;

public class XmlUtilTest {

    @org.junit.Test
    public void testFromXML() throws Exception {
        String xml = "<parent>\n" +
                "  <name>John</name>\n" +
                "  <age>40</age>\n" +
                "  <children>\n" +
                "    <child>\n" +
                "      <name>Micheal</name>\n" +
                "      <age>12</age>\n" +
                "    </child>\n" +
                "    <child>\n" +
                "      <name>Jessica</name>\n" +
                "      <age>7</age>\n" +
                "    </child>\n" +
                "  </children>\n" +
                "</parent>";

        Parent john = XmlUtil.fromXML(Parent.class, xml);
        System.out.println(john);
    }

    @org.junit.Test
    public void testToXML() throws Exception {
        Parent john = new Parent();
        john.setAge(40);
        john.setName("John");

        Child micheal = new Child();
        micheal.setAge(12);
        micheal.setName("Micheal");
        Child jessica = new Child();
        jessica.setAge(7);
        jessica.setName("Jessica");

        List<Child> children = new ArrayList<Child>();
        children.add(micheal);
        children.add(jessica);
        john.setChildren(children);

        String xml = XmlUtil.toXML(john);
        System.out.println(xml);
    }

    /********************************************************************
     ここから下は、上記のテストメソッドで使用している
     XMLに相互変換するクラスの定義です。
     *********************************************************************/
    @XStreamAlias("parent")
    public static class Parent {

        @XStreamAlias("name")
        private String name;

        @XStreamAlias("age")
        private int age;

        @XStreamAlias("children")
        private List<Child> children;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        public List<Child> getChildren() {
            return children;
        }

        public void setChildren(List<Child> children) {
            this.children = children;
        }

        @Override
        public String toString() {
            return "Parent{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", children=" + children +
                    '}';
        }
    }

    @XStreamAlias("child")
    public static class Child {

        @XStreamAlias("name")
        private String name;

        @XStreamAlias("age")
        private int age;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        @Override
        public String toString() {
            return "Child{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
}

サンプルでは、Parentクラスのインスタンスxmlにしたり、xmlからParentインスタンスをつくってます。

XMLの要素名は、何もしないと、クラス名とか変数名になります。

個人的な好みで、XMLに、キャメルケースを使用したくなかったので、@XStreamAliasというアノテーションをつけ、XMLに任意の要素名をつけることにしました。

また、@XStreamAliasを使用するためには、以下のメソッドを実行して、アノテーションをONにしてやります。

xstream.processAnnotations(obj.getClass());


んで、それぞれSYSOしているところの出力は、こちらです。

testFromXML

<parent>
  <name>John</name>
  <age>40</age>
  <children>
    <child>
      <name>Micheal</name>
      <age>12</age>
    </child>
    <child>
      <name>Jessica</name>
      <age>7</age>
    </child>
  </children>
</parent>

testToXML

Parent{name='John', age=40, children=[Child{name='Micheal', age=12}, Child{name='Jessica', age=7}]}

Parentは、childrenというChildクラスのListをもっていますが、ちゃんと表現されていますね。

ここにはありませんが、他にも、Mapも通常の配列[]もいけるようです。
どこまで、うまく変換できるのか興味深いところですね。

Sample Source: ryu1/utils · GitHub