LoginSignup
253

JAXB使い方メモ

Last updated at Posted at 2013-10-20

JAXB(Java Architecture for XML Binding)の使い方メモ。

JAXB とは、 XML と Java オブジェクトを相互変換するための API 仕様のこと。
Java SE6 からは標準ライブラリに組み込まれているので、特に jar を追加することなく使える。
Java 11 より、 JAXB は標準ライブラリから削除されました(Java EE には引き続き含まれています)。

Java SE 11 以降の環境で JAXB を使う

JAXB はもともと Java EE の一部だった。
一時は SE に入れられたが、 Java 11 で EE 系のクラスが SE から削除されたときに、一緒に SE から削除された。

SE 11 以降の環境で JAXB を使用したい場合は、ライブラリとして追加する必要がある。

2019 年現在の、JAXB の参照実装は たぶんこれ
ここのドキュメント を見た感じ、次のアーティファクトを依存関係に入れれば利用できる。

build.gradle
dependencies {
    implementation "javax.xml.bind:jaxb-api:2.3.1"
    implementation "org.glassfish.jaxb:jaxb-runtime:2.3.1"
}

javax.xml.bind:jaxb-api が、 JAXB の API を含むアーティファクトで、
org.glassfish.jaxb:jaxb-runtime が JAXB の実装になる。

Jakarta EE での利用

Java EE から Jakarta EE に変わったことで、ライブラリやパッケージ名が変わった。

Jakarta EE を使う場合は次のアーティファクトを指定する。

build.gradle
dependencies {
    implementation "jakarta.xml.bind:jakarta.xml.bind-api:4.0.0"
    implementation "com.sun.xml.bind:jaxb-impl:4.0.3"
}

ランタイムには Eclipse から提供されている互換実装(Compatible Implementation)を指定している。
参考:https://eclipse-ee4j.github.io/jaxb-ri/

本記事のコード例は、すべて Java EE のパッケージ名 (javax.xml.bind...) で記載している。
Jakarta EE 版を利用する場合は、適宜 jakarta.xml.bind... パッケージに読み替えること。

オブジェクト → XML

変換元Javaクラス
public class Hoge {
     
    private int id;
    private String value;
     
    // Getter, Setter は省略
}
変換処理
import javax.xml.bind.JAXB;
 
public class JAXBSample {
 
    public static void main(String[] args) {
        Hoge hoge = new Hoge();
        hoge.setId(10);
        hoge.setValue("hoge");
         
        JAXB.marshal(hoge, System.out);
    }
}
変換結果
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<hoge>
    <id>10</id>
    <value>hoge</value>
</hoge>

XML → オブジェクト

変換処理
public static void main(String[] args) {
    String xml = "<?xml version=\"1.0\"?>"
               + "<hoge>"
               + "  <id>20</id>"
               + "  <value>hogehoge</value>"
               + "</hoge>";
     
    Hoge hoge = JAXB.unmarshal(new StringReader(xml), Hoge.class);
     
    System.out.println("id=" + hoge.getId() + ", value=" + hoge.getValue());
}
出力結果
id=20, value=hogehoge

アノテーションを使って調整する

フィールドをタグの属性と紐付ける

XmlAttributeアノテーションで属性と紐付ける
import javax.xml.bind.annotation.XmlAttribute;
 
public class Hoge {
     
    private int id;
    private String value;
     
    @XmlAttribute
    public int getId() {
        return id;
    }
     
    // 以下 Getter, Setter 省略
}
XML変換結果
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<hoge id="10">
    <value>hoge</value>
</hoge>

XmlAttribute アノテーションを、属性と紐付けたいフィールドの getterメソッド に付与する。

タグと属性の名前を任意の値に変更する

各アノテーションのnameで名前を指定する
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
 
public class Hoge {
     
    private int id;
    private String value;
     
    @XmlAttribute(name="hoge-id")
    public int getId() {
        return id;
    }
    @XmlElement(name="hoge-value")
    public String getValue() {
        return value;
    }
     
    // Setter メソッドは省略
}
XML変換結果
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<hoge hoge-id="10">
    <hoge-value>hoge</hoge-value>
</hoge>

タグ名の場合は XmlElement アノテーションの name に、
属性名の場合は XmlAttribute アノテーションの name にそれぞれ設定したい名前を指定する。

要素の順序の決定

順序を決定するためには、 XmlType アノテーションの propOrder を付与する。

順序の決定
@XmlType(propOrder={"one", "two", "three"})
public class Hoge {
    private String one = "1";
    private String three = "3";
    private String two = "2";

    // Getter, Setter は省略
}
XML変換結果(デフォルト)
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<hoge>
    <one>1</one>
    <two>2</two>
    <three>3</three>
</hoge>

toenobu さんに追記していただきました。ありがとうございます。

ルートタグの名前を変更する

XmlRootElementアノテーションでルートタグの名前を変更する
import javax.xml.bind.annotation.XmlRootElement;
 
@XmlRootElement(name="hoge-tag")
public class Hoge {
    private int id;
    private String value;
     
    // Getter, Setter 省略
}
XML変換結果
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<hoge-tag>
    <id>10</id>
    <value>hoge</value>
</hoge-tag>

XML のルートタグとなるクラスに XmlRootElement アノテーションを付与し、 name に設定したいタグの名前を指定する。

Listの変換をいい感じにする

List<String> のような List のフィールドを XML に変換すると、デフォルトだと以下のようになる。

Listフィールドを持つクラス
public class Hoge {
    private List<String> listValue;
     
    // Getter, Setter は省略
}
XML変換結果(デフォルト)
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<hoge>
    <listValue>aaa</listValue>
    <listValue>bbb</listValue>
    <listValue>ccc</listValue>
</hoge>

List の中身が同じ高さで出力されて格好悪い。

できれば以下のようにしたい。

XML変換結果(希望形)
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<hoge>
    <list>
        <value>aaa</value>
        <value>bbb</value>
        <value>ccc</value>
    </list>
</hoge>

上記のように変換するためには、次のように Java クラスでアノテーションを設定する。

希望形で変換させるためのアノテーション設定
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
 
public class Hoge {
    private List<String> listValue;
     
    @XmlElementWrapper(name="list")
    @XmlElement(name="value")
    public List<String> getListValue() {
        return listValue;
    }
    // Setter 省略
}

XmlElementWrapper アノテーションの nameList の各要素タグをラップするタグの名前を、
XmlElement アノテーションの name に要素タグの名前を設定する。

特定のフィールドを変換対象外にする

XmlTransientアノテーションで対象外を指定する
import javax.xml.bind.annotation.XmlTransient;
 
public class Hoge {
     
    private int id;
    private String value;
     
    @XmlTransient
    public String getValue() {
        return value;
    }
     
    // Getter, Setter 省略
}
XML変換結果
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<hoge>
    <id>10</id>
</hoge>

XmlTransient アノテーションを付与したフィールドは XML 変換対象外になる。

enum をマッピングする

MyClass
package jaxb;

public class MyClass {
    
    private MyEnum hoge = MyEnum.HOGE;
    private MyEnum fuga = MyEnum.FUGA;
    private MyEnum piyo = MyEnum.PIYO;
    
    // getter, setter 省略
}
MyEnum
package jaxb;

import javax.xml.bind.annotation.XmlEnumValue;

public enum MyEnum {
    HOGE,
    @XmlEnumValue("fuga") FUGA,
    @XmlEnumValue("Piyo") PIYO,
}
XML変換結果
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<myClass>
    <fuga>fuga</fuga>
    <hoge>HOGE</hoge>
    <piyo>Piyo</piyo>
</myClass>

enum は、デフォルトでは列挙子の名前を文字列にした値とマッピングされる(HOGE)。

任意の値とマッピングさせたい場合は、 enum の列挙子に対して @XmlEnumValue を付与し、 value にマッピングしたい文字列を指定する(fuga, Piyo)。

1つのインターフェースフィールドに対して、複数の実装クラスをマッピングする

MyInterface.java
package jaxb;

public interface MyInterface {
    void hello();
}
Hoge.java
package jaxb;

import java.util.List;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlElements;

public class Hoge {
    
    private List<MyInterface> list;
    private MyInterface any;

    @XmlElementWrapper(name="list")
    @XmlElements({
        @XmlElement(name="fuga", type=Fuga.class),
        @XmlElement(name="piyo", type=Piyo.class)
    })
    public List<MyInterface> getList() {
        return this.list;
    }

    @XmlElements({
        @XmlElement(name="fuga", type=Fuga.class),
        @XmlElement(name="piyo", type=Piyo.class)
    })
    public MyInterface getAny() {
        return this.any;
    }

    // setter 省略
}
Fuga.java
package jaxb;

public class Fuga implements MyInterface {
    
    private int num;

    @Override
    public void hello() {
        System.out.println("fuga : num = " + this.num);
    }

    // setter, getter 省略
}
Piyo.java
package jaxb;

import javax.xml.bind.annotation.XmlAttribute;

public class Piyo implements MyInterface {

    private String value;
    
    @Override
    public void hello() {
        System.out.println("hoge : value = " + this.value);
    }

    @XmlAttribute
    public String getValue() {
        return this.value;
    }
    
    // setter 省略
}

MyInterface インターフェースを実装した Fuga クラスと Piyo クラスがある。

Hoge クラスでは MyInterface 型でフィールドを定義して、 @XmlElements({@XmlElement(...), ...}) という形でタグ名と実装クラスのマッピングを定義している。

上記クラスは、↓のような XML に変換できる。

test.xml
<?xml version="1.0" encoding="UTF-8"?>
<hoge>
  <fuga>
    <num>123</num>
  </fuga>
  
  <list>
    <fuga>
      <num>543</num>
    </fuga>
    <piyo value="piyopiyo" />
  </list>
</hoge>

↑の XML を読み込んで、 MyInterface#hello() を実行すると、↓のようになる。

JaxbMain.java
package jaxb;

import java.io.File;
import javax.xml.bind.JAXB;

public class JaxbMain {
    
    public static void main(String[] args) {
        Hoge hoge = JAXB.unmarshal(new File("test.xml"), Hoge.class);
        
        hoge.getAny().hello();
        System.out.println();
        
        for (MyInterface any : hoge.getList()) {
            any.hello();
        }
    }
}
実行結果
fuga : num = 123

fuga : num = 543
hoge : value = piyopiyo

名前空間が宣言されている XML をマッピングする

次のように名前空間が宣言されている XML をマッピングする。

XML
<?xml version="1.0" encoding="UTF-8"?>
<entity xmlns="hoge-namespace" xmlns:fuga="fuga-namespace">
  <id>20</id>
  <fuga:value>VALUE</fuga:value>
</entity>

フィールドごとに名前空間を設定する

Entity.java
package sample.jaxb;

import javax.xml.bind.annotation.XmlElement;

public class Entity {
    
    private int id;
    private String value;
    
    @XmlElement(namespace="hoge-namespace")
    public int getId() {
        return this.id;
    }

    @XmlElement(namespace="fuga-namespace")
    public String getValue() {
        return this.value;
    }

    // setter 省略
}

@XmlElement アノテーションの namespace 属性で名前空間を指定する。

namespace 属性は、 @XmlAttribute アノテーションにもある。

package-info.java で共通の名前空間を設定する

package-info.java@XmlSchema アノテーションを付けることで、パッケージ内に存在する全てのクラスに対して名前空間を一括で設定できる。

package-info.java
@XmlSchema(
    namespace="hoge-namespace",
    elementFormDefault=XmlNsForm.QUALIFIED
)
package sample.jaxb;

import javax.xml.bind.annotation.XmlSchema;
import javax.xml.bind.annotation.XmlNsForm;
Entity.java
package sample.jaxb;

import javax.xml.bind.annotation.XmlElement;

public class Entity {
    
    private int id;
    private String value;

    @XmlElement(namespace="fuga-namespace")
    public String getValue() {
        return this.value;
    }

    // getter, setter 省略
}

hoge-namespacepackage-info.java@XmlSchema アノテーションで指定しているので、 Entity.java では名前空間の指定を省略できる。

ある名前空間に属するタグが複数存在する場合は、この方法を使うと設定が1カ所にまとめられて楽になる。

ただし、 package-info.java で指定した設定が適用されるのは、そのパッケージ直下のクラスだけなので注意。

Java オブジェクトから XML に変換するときの QName のプレフィックスを指定する

名前空間が設定された状態で Java オブジェクトを XML に変換すると、以下のように QName (qualified name=修飾された名前)のプレフィックスに適当な値が使用される。

Entity.java
package sample.jaxb;

import javax.xml.bind.annotation.XmlElement;

public class Entity {
    
    private int id;
    private String value;
    
    @XmlElement(namespace="hoge-namespace")
    public int getId() {
        return this.id;
    }

    @XmlElement(namespace="fuga-namespace")
    public String getValue() {
        return this.value;
    }

    // setter 省略
}
JaxbMain.java
package sample.jaxb;

import javax.xml.bind.JAXB;


public class JaxbMain {
    
    public static void main(String[] args) {
        Entity entity = new Entity();
        entity.setId(12);
        entity.setValue("ENTITY");
        
        JAXB.marshal(entity, System.out);
    }
}
実行結果
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<entity xmlns:ns2="fuga-namespace" xmlns:ns3="hoge-namespace">
    <ns3:id>12</ns3:id>
    <ns2:value>ENTITY</ns2:value>
</entity>

ns2ns3 という適当なプレフィックスが設定されている。

プレフィックスを任意の値にしたい場合は、 package-info.java@XmlSchema アノテーションで以下のように設定する。

package-info.java
@XmlSchema(
    xmlns={
        @XmlNs(prefix="hoge", namespaceURI="hoge-namespace"),
        @XmlNs(prefix="fuga", namespaceURI="fuga-namespace")
    }
)
package sample.jaxb;

import javax.xml.bind.annotation.XmlSchema;
import javax.xml.bind.annotation.XmlNs;
変換結果
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<entity xmlns:fuga="fuga-namespace" xmlns:hoge="hoge-namespace">
    <hoge:id>12</hoge:id>
    <fuga:value>ENTITY</fuga:value>
</entity>

プレフィックスが hogefuga に変わっている。

参考

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
253