JAXB 2.0
を興味本位で勉強したり、仕事では、
- JDOM
- Apache Commons Digester(なつかしいStrutsの内部でも使われてたから)
- Apache Commons Configuration(XPathでXML設定ファイルの値を取得できる)
使ってみたり、特に、Axis2をつかったときは、
- XMLBeans
- JiBX
をちらっとやったり(結局は、Axis2独自のADBを使った)いわゆるXMLデータバインディングという類いのライブラリをつかったりしていた。
Java 6がでて随分たつけど、JAXBさわったことがなかった。今の職場でJAX-RSが使われてて、アノテーションを指定してXMLを処理するのに興味がでてきたので、ためしにJAXBをさわってみる。
ドキュメントはここなんだろうか。
http://java.sun.com/javase/ja/6/docs/ja/technotes/guides/xml/jaxb/index.html
Javadocは、
http://java.sun.com/javase/ja/6/docs/ja/api/javax/xml/bind/Marshaller.html
http://java.sun.com/javase/ja/6/docs/ja/api/javax/xml/bind/Unmarshaller.html
http://java.sun.com/javase/ja/6/docs/ja/api/javax/xml/bind/JAXB.html
あたりをよめばいけそうだ。
JAXBクラスが、便利クラスなわけか。
アノテーションの使い方など、例を使ったJAXBの説明はここが一番わかりやすかった気がする。
http://jaxb.java.net/guide/
ドキュメントを調べたので、実装してみよう。googleでJAXBを調べてみると、たいていは、XML Schemaを書いて、xjcコマンドで、Javaのクラスを生成してというのが多い。自分は、XML Schemaに明るくないので、手作業でJavaのクラスを書くことにしよう。
ちょっと準備
XMLをJavaのオブジェクトにするクラスを準備する。
こいつは、共通。
XMLの読み込んでJavaにマッピングしたい場合は、JAXBクラスのunmarshallを使う。
その逆は、marshall
package note.jaxb20; import java.io.File; import javax.xml.bind.JAXB; public final class XmlBinder { private XmlBinder() { } public static <T> T xmlToObjcet(File file, Class<T> clazz) { return JAXB.unmarshal(file, clazz); } }
XMLとJavaのクラス
<?xml version="1.0" encoding="UTF-8"?> <persons> <person no="1"> <first-name>テスト名1</first-name> <family-name>テスト姓1</family-name> <!-- 日時は、W3C-DTFのフォーマットでXMLに記述してあれば、 勝手にjava.util.Dateに変換してくれるようだ。 --> <birthday>1980-01-23</birthday> <sex>男</sex> <favorite-things> <thing>女</thing> <thing>酒</thing> </favorite-things> </person> <person no="2"> <first-name>テスト名2</first-name> <family-name>テスト姓2</family-name> <!-- 日時は、W3C-DTFのフォーマットでXMLに記述してあれば、 勝手にjava.util.Dateに変換してくれるようだ。 --> <birthday>1990-01-23</birthday> <sex>女</sex> <favorite-things> <thing>金</thing> <thing>ファッション</thing> <thing>音楽</thing> <thing>旅行</thing> </favorite-things> </person> </persons>
package note.jaxb20.example01; import java.util.ArrayList; import java.util.List; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; import org.apache.commons.lang.builder.ToStringBuilder; @XmlRootElement(name = "persons") public class Persons { private List<Person> personList = new ArrayList<Person>(); @XmlElement(name = "person") public List<Person> getPersonList() { return personList; } public void setPersonList(List<Person> personList) { this.personList = personList; } @Override public int hashCode() { return HashCodeBuilder.reflectionHashCode(this); } @Override public boolean equals(Object obj) { return EqualsBuilder.reflectionEquals(this, obj); } @Override public String toString() { return ToStringBuilder.reflectionToString(this); } }
package note.jaxb20.example01; import java.util.ArrayList; import java.util.Date; import java.util.List; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlType; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; import org.apache.commons.lang.builder.ToStringBuilder; @XmlType(name = "person") public class Person { private Integer no; private String firstName; private String familyName; private Date birthDay; private String sex; private List<Thing> favoriteThings = new ArrayList<Thing>(); @XmlAttribute public Integer getNo() { return no; } public void setNo(Integer no) { this.no = no; } @XmlElement(name = "first-name") public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } @XmlElement(name = "family-name") public String getFamilyName() { return familyName; } public void setFamilyName(String familyName) { this.familyName = familyName; } @XmlElement(name = "birthday") public Date getBirthDay() { return birthDay; } public void setBirthDay(Date birthDay) { this.birthDay = birthDay; } @XmlElement public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } @XmlElement(name = "thing") @XmlElementWrapper(name = "favorite-things") public List<Thing> getFavoriteThings() { return favoriteThings; } public void setFavoriteThings(List<Thing> favoriteThings) { this.favoriteThings = favoriteThings; } @Override public int hashCode() { return HashCodeBuilder.reflectionHashCode(this); } @Override public boolean equals(Object obj) { return EqualsBuilder.reflectionEquals(this, obj); } @Override public String toString() { return ToStringBuilder.reflectionToString(this); } }
package note.jaxb20.example01; import javax.xml.bind.annotation.XmlType; import javax.xml.bind.annotation.XmlValue; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; import org.apache.commons.lang.builder.ToStringBuilder; @XmlType(name = "thing") public class Thing { private String name; @XmlValue public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public int hashCode() { return HashCodeBuilder.reflectionHashCode(this); } @Override public boolean equals(Object obj) { return EqualsBuilder.reflectionEquals(this, obj); } @Override public String toString() { return ToStringBuilder.reflectionToString(this); } }
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import java.io.File; import java.text.ParseException; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import note.jaxb20.XmlBinder; import note.jaxb20.example01.Person; import note.jaxb20.example01.Persons; import note.jaxb20.example01.Thing; import note.jaxb20.example02.Code; import note.jaxb20.example02.CodeList; import org.apache.commons.lang.time.DateUtils; import org.junit.Test; public class XmlBinderTest { private File dir = new File("test-data"); public void setUp() { } @Test public void testExample01() { Persons actual = XmlBinder.xmlToObjcet(new File(dir, "persons.xml"), Persons.class); List<Person> personList = actual.getPersonList(); assertNotNull(personList); assertEquals(2, personList.size()); Person person0 = personList.get(0); assertNotNull(person0); assertEquals(Integer.valueOf(1), person0.getNo()); Person person1 = personList.get(1); assertNotNull(person1); assertEquals(Integer.valueOf(2), person1.getNo()); assertEquals(parse("1990-01-23", "yyyy-MM-dd"), person1.getBirthDay()); List<Thing> favoriteThings = person1.getFavoriteThings(); assertNotNull(favoriteThings); assertEquals(4, favoriteThings.size()); assertEquals("金", favoriteThings.get(0).getName()); } private static Date parse(String dateAsString, String pattern) { try { return DateUtils.parseDate(dateAsString, new String[] { pattern }); } catch (ParseException e) { throw new RuntimeException(e); } } }
アノテーションの使い方で、多少、試行錯誤が必要だったものの、
あっさりバインディングできた。
らくちん!XML Schemaからツールで自動生成したのでない手書きのクラスやアノテーションでもちゃんと動くもんだなー。
こんなのもできる
entry, key, valueという要素名を使えば、デフォルトでもMapにXMLを読み込んでくれる。
ただし、型はMapインタフェースでなくて、HashMapのようにクラスじゃないとだめなようだ。
<?xml version="1.0" encoding="UTF-8"?> <code-list> <code name="sex"> <entries> <entry> <key>1</key> <value>女</value> </entry> <entry> <key>2</key> <value>男</value> </entry> </entries> </code> <code name="size"> <entries> <entry> <key>1</key> <value>小</value> </entry> <entry> <key>2</key> <value>中</value> </entry> <entry> <key>3</key> <value>大</value> </entry> </entries> </code> </code-list>
package note.jaxb20.example02; import java.util.ArrayList; import java.util.List; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; import org.apache.commons.lang.builder.ToStringBuilder; @XmlRootElement(name = "code-list") public class CodeList { private List<Code> codeList = new ArrayList<Code>(); @XmlElement(name = "code") public List<Code> getCodeList() { return codeList; } public void setCodeList(List<Code> codeList) { this.codeList = codeList; } @Override public int hashCode() { return HashCodeBuilder.reflectionHashCode(this); } @Override public boolean equals(Object obj) { return EqualsBuilder.reflectionEquals(this, obj); } @Override public String toString() { return ToStringBuilder.reflectionToString(this); } }
package note.jaxb20.example02; import java.util.HashMap; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlType; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; import org.apache.commons.lang.builder.ToStringBuilder; @XmlType(name = "code") public class Code { private String name; private HashMap<Integer, String> entries = new HashMap<Integer, String>(); @XmlAttribute public String getName() { return name; } public void setName(String name) { this.name = name; } @XmlElementWrapper public HashMap<Integer, String> getEntries() { return entries; } public void setEntries(HashMap<Integer, String> entries) { this.entries = entries; } @Override public int hashCode() { return HashCodeBuilder.reflectionHashCode(this); } @Override public boolean equals(Object obj) { return EqualsBuilder.reflectionEquals(this, obj); } @Override public String toString() { return ToStringBuilder.reflectionToString(this); } }
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import java.io.File; import java.text.ParseException; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import note.jaxb20.XmlBinder; import note.jaxb20.example01.Person; import note.jaxb20.example01.Persons; import note.jaxb20.example01.Thing; import note.jaxb20.example02.Code; import note.jaxb20.example02.CodeList; import org.apache.commons.lang.time.DateUtils; import org.junit.Test; public class XmlBinderTest { private File dir = new File("test-data"); public void setUp() { } @Test public void testExample02() { HashMap<Integer, String> entries0 = new HashMap<Integer, String>(); entries0.put(1, "女"); entries0.put(2, "男"); Code code0 = new Code(); code0.setName("sex"); code0.setEntries(entries0); HashMap<Integer, String> entries1 = new HashMap<Integer, String>(); entries1.put(1, "小"); entries1.put(2, "中"); entries1.put(3, "大"); Code code1 = new Code(); code1.setName("size"); code1.setEntries(entries1); List<Code> codeList = new ArrayList<Code>(); codeList.add(code0); codeList.add(code1); CodeList expected = new CodeList(); expected.setCodeList(codeList); CodeList actual = XmlBinder.xmlToObjcet(new File(dir, "code-list.xml"), CodeList.class); assertEquals(expected, actual); } }