JAXBはJava Architecture for XML
Bindingの略で、XMLのデータとJavaのクラスを紐付ける(バインディングする・デシリアライズする)ことが出来る。
JAXBはJava6(JDK1.6)からJavaの標準APIの仲間入りをしたらしい。(なので特別なライブラリーを入れなくても使用できる)
Java11でJAXBは標準APIから削除された。[2018-10-01]
以下のようなXMLファイルを読み込む例。
<?xml version="1.0" encoding="UTF-8"?> <root> <record><column1>aaa</column1><column2>111</column2></record> <record><column1>bbb</column1><column2>222</column2></record> </root>
まず、読み込んだXMLデータを保持するクラス(JavaBeans)を用意する。
import java.util.List; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "root")
public class ExampleBean {
private List<RecordBean> recordList;
@XmlElement(name = "record")
public List<RecordBean> getRecordList() {
return recordList;
}
public void setRecordList(List<RecordBean> recordList) {
this.recordList = recordList;
}
@Override
public String toString() {
if (recordList == null) {
return "null[]";
}
return recordList.toString();
}
}
XMLデータのルート要素名を@XmlRootElementで指定する。
今回の例ではルート要素内にrecordタグが複数入っているので、ひとつのrecordタグのデータを表すRecordBeanクラスを用意し、
ExampleBeanではRecordBeanのリストを保持する。
@XmlElementをゲッターメソッドに付けてタグ名を指定する。(セッターメソッドに付けても大丈夫そうだったけど)
public class RecordBean {
private String column1;
private String column2;
public String getColumn1() {
return column1;
}
public void setColumn1(String column1) {
this.column1 = column1;
}
public String getColumn2() {
return column2;
}
public void setColumn2(String column2) {
this.column2 = column2;
}
@Override
public String toString() {
return String.format("(%s,%s)", column1, column2);
}
}
RecordBeanクラスは、recordタグひとつ分のデータを保持するJavaBean。
column1,column2といったタグ名とJavaBeanのプロパティー名が一致していれば、特にアノテーションを付ける必要は無い。
import java.io.IOException; import java.io.InputStream; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller;
public class XmlJaxbExample {
public static void main(String... args) throws IOException {
new XmlJaxbExample().read("example.xml");
}
public void read(String fileName) throws IOException {
ExampleBean example;
try (InputStream is = getClass().getResourceAsStream(fileName)) {
JAXBContext context = JAXBContext.newInstance(ExampleBean.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
example = (ExampleBean) unmarshaller.unmarshal(is);
} catch (JAXBException e) {
throw new IOException(e);
}
System.out.println(example);
}
}
まず、JAXBContextのnewInstanceメソッドを呼び出してJAXBContextインスタンスを生成する。
このときにXMLのスキーマ情報を渡す必要があるようだが、今回は変換先のJavaBeanクラスを指定している。
そしてJAXBContextからUnmarshallerを生成し、unmarshalメソッドを呼び出す。
するとXMLファイルを読み込んでJavaのインスタンスに変換してくれる。
(unmarshalメソッドの戻り型はObjectなので、自分でキャストする必要がある)
InputStreamはunmarshalメソッド内でクローズされるようだが、念のためにtry-with-resources構文でInputStreamをクローズしている。
JAXBクラスを使うと、もっとシンプルに書ける。
(JAXBContextやUnmarshallerの生成が内部で自動的に行われる)
import javax.xml.bind.DataBindingException; import javax.xml.bind.JAXB;
public void read2(String fileName) throws IOException {
ExampleBean example;
try (InputStream is = getClass().getResourceAsStream(fileName)) {
example = JAXB.unmarshal(is, ExampleBean.class);
} catch (DataBindingException e) {
throw new IOException(e);
}
System.out.println(example);
}
以下のようなXMLファイルを出力する例。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<record>
<column1>aaa</column1>
<column2>111</column2>
</record>
<record>
<column1>bbb</column1>
<column2>222</column2>
</record>
</root>
import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller;
public class XmlJaxbExample {
public static void main(String... args) throws IOException {
List<RecordBean> list = new ArrayList<>();
list.add(new RecordBean("aaa", "111")); // RecordBeanにコンストラクターを追加した
list.add(new RecordBean("bbb", "222"));
ExampleBean example = new ExampleBean();
example.setRecordList(list);
new XmlJaxbExample().write("D:/temp/example.xml", example);
}
public void write(String fileName, ExampleBean example) throws IOException {
try (FileOutputStream os = new FileOutputStream(fileName)) {
JAXBContext context = JAXBContext.newInstance(example.getClass());
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); // 整形して出力する
marshaller.marshal(example, os);
} catch (JAXBException e) {
throw new IOException(e);
}
}
}
JAXBContextインスタンスの生成方法はXML読み込みの例と同じ。
JAXBContextからMarshallerを生成してmarshalメソッドを呼び出すと、XMLファイルが出力される。
このとき、JAXB_FORMATTED_OUTPUTをtrueにしないと、出力されたデータは一切改行されず(当然インデントも入らず)、1行のみのXMLファイルになる。
marshalメソッド内では出力先のリソース(OutputStream)をクローズしないので、自分で(try-with-resources構文で)OutputStreamをクローズする必要がある。
JAXBクラスを使うと、もっとシンプルに書ける。
(JAXBContextやMarshallerの生成が内部で自動的に行われる)
import java.io.File; import javax.xml.bind.DataBindingException; import javax.xml.bind.JAXB;
public void write2(String fileName, ExampleBean example) throws IOException {
try (FileOutputStream os = new FileOutputStream(fileName)) {
JAXB.marshal(example, os);
} catch (DataBindingException e) {
throw new IOException(e);
}
}
public void write3(String fileName, ExampleBean example) throws IOException {
try {
JAXB.marshal(example, new File(fileName));
} catch (DataBindingException e) {
throw new IOException(e);
}
}
この場合、JAXB_FORMATTED_OUTPUTはtrueになっている。