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になっている。