タグがラップされているXMLを読み込む

こんにちは。kkです。

プロジェクトでちょっとXMLを読み込む機会があったので、今回はその中で少しハマった所とその対応を紹介します。

XML→オブジェクトの変換はJAXBを使用します。
https://docs.oracle.com/javase/jp/8/api/javax/xml/bind/JAXB.html
https://ja.wikipedia.org/wiki/Java_Architecture_for_XML_Binding

まずは簡単なXMLの変換

  • ReadXmlSample.java

JAXB.unmarshal を使うことにより、XMLからJavaオブジェクトへマッピングしてくれます。

[java]
package jp.co.opentone.kikuchi.sample.xml;

import java.io.StringReader;

import javax.xml.bind.JAXB;

public class ReadXmlSample {

    public static void main(String[] args) {

        // XMLデータ
        String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
                + "<data>"                          // データ情報
                + "  <dataID>1</dataID>"            // データID
                + "  <dataName>でーた1</dataName>" // データ名
                + "</data>"
                + "";

        SampleData data = JAXB.unmarshal(new StringReader(xml), SampleData.class);
        System.out.println(data.toString());
    }
}
[/java]
  • SampleData.java

data をマッピングするクラスです。

[java]
package jp.co.opentone.kikuchi.sample.xml;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "data")
public class SampleData {

    @XmlElement(name = "dataID")
    private int dataId;

    @XmlElement(name = "dataName")
    private String dataName;

    // 略
}
[/java]

出力結果はこちら。

[text]
SampleData [dataId=1, dataName=でーた1]
[/text]

簡単ですね。

次に、dataが複数ある場合…

  • ReadXmlSample.java
[java]
package jp.co.opentone.kikuchi.sample.xml;

import java.io.StringReader;

import javax.xml.bind.JAXB;

public class ReadXmlSample {

    public static void main(String[] args) {

        // XMLデータ
        String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
                + "<dataList>"
                + "  <data>"                          // データ情報
                + "    <dataID>1</dataID>"            // データID
                + "    <dataName>でーた1</dataName>" // データ名
                + "  </data>"
                + "  <data>"
                + "    <dataID>2</dataID>"
                + "    <dataName>でーた2</dataName>"
                + "  </data>"
                + "  <data>"
                + "    <dataID>3</dataID>"
                + "    <dataName>でーた3</dataName>"
                + "  </data>"
                + "</dataList>"
                + "";

        SampleResponse response = JAXB.unmarshal(new StringReader(xml), SampleResponse.class);
        for (SampleData data: response.getDataList()) {
            System.out.println(data.toString());
        }
    }
}
[/java]
  • SampleResponse.java

dataList をマッピングするクラスです。※data の List

[java]
package jp.co.opentone.kikuchi.sample.xml;

import java.util.List;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;

@XmlAccessorType(XmlAccessType.FIELD)
public class SampleResponse {

    // ここで指定する要素は、dataList では無く、data
    @XmlElement(name = "data")
    List<SampleData> dataList;

    // 略
}
[/java]

※SampleData.java は変更無し

出力結果はこちら。

[text]
SampleData [dataId=1, dataName=でーた1]
SampleData [dataId=2, dataName=でーた2]
SampleData [dataId=3, dataName=でーた3]
[/text]

出来てますね。

最後に、一番外側を1つのタグでくくり、その中に複数要素が有り、かつリストになっている場合(日本語だと分かりにくいのでデータ参照)

  • ReadXmlSample.java
[java]
package jp.co.opentone.kikuchi.sample.xml;

import java.io.StringReader;

import javax.xml.bind.JAXB;

public class ReadXmlSample {

    public static void main(String[] args) {

        // XMLデータ
        String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
                + "<response>"                          // レスポンス情報
                + "  <result>"                          // 結果情報
                + "    <status>1</status>"              // 処理結果ステータス
                + "    <count>3</count>"                // 取得件数
                + "  </result>"
                + "  <dataList>"                           // データ情報のリスト
                + "    <data>"                          // データ情報
                + "      <dataID>1</dataID>"            // データID
                + "      <dataName>でーた1</dataName>" // データ名
                + "    </data>"
                + "    <data>"
                + "      <dataID>2</dataID>"
                + "      <dataName>でーた2</dataName>"
                + "    </data>"
                + "    <data>"
                + "      <dataID>3</dataID>"
                + "      <dataName>でーた3</dataName>"
                + "    </data>"
                + "  </dataList>"
                + "</response>"
                + "";

        SampleResponse response = JAXB.unmarshal(new StringReader(xml), SampleResponse.class);
        System.out.println(response.getResult().toString());
        for (SampleData data: response.getDataList()) {
            System.out.println(data.toString());
        }
    }
}
[/java]
  • SampleResult.java

result 要素のマッピング用クラスです。

[java]
package jp.co.opentone.kikuchi.sample.xml;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "result")
public class SampleResult {

    @XmlElement(name = "status")
    private int status;

    @XmlElement(name = "count")
    private int count;

    // 略
}
[/java]
  • SampleResponse.java

result の追加と、dataList フィールドに対して @XmlElementWrapper アノテーションを付加します。
@XmlElementWrapper に指定する name は、dataList になります。

[java]
package jp.co.opentone.kikuchi.sample.xml;

import java.util.List;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "response")
public class SampleResponse {

    @XmlElement(name = "result")
    SampleResult result;

    // dataList の名前で @XmlElementWrapper を付加
    @XmlElementWrapper(name = "dataList")
    @XmlElement(name = "data")
    List<SampleData> dataList;

    // 略
}
[/java]

※SampleData.java は変更無し

出力結果はこちら。

[text]
SampleResult [status=1, count=3]
SampleData [dataId=1, dataName=でーた1]
SampleData [dataId=2, dataName=でーた2]
SampleData [dataId=3, dataName=でーた3]
[/text]

タグ構成として、
response
└dataList
 └data
となっているので、@XmlElementWrapper で dataList を指定しないと正常に読み込めなくなっています。
※今回ちょっとハマった箇所

以上、タグがラップされている状態での対応方法でした。