kurukuru-papaのブログ

主に、ソフトウェア開発に関連したメモを書き溜めたいと思います。

JAXBでネームスペース付加・XMLスキーマ検証もできました

JAXB(Java Architecture for XML Binding)で、ネームスペースを付加したり、XMLスキーマ検証をしてみました。少し悩んだところもありましたが、問題なく動作確認できました。

ネームスペース付加

オブジェクト→XML変換

変換元データを保持するクラスは、特別なことはありません。ネームスペースを付加しない場合と同じ作りです。

変換元データを保持するクラス

@XmlRootElement(name = "person")
public class PersonWithXmlns {
    // Getter/Setterを省略して、publicにしてあります。
    public String name;
    public int age;
}

ポイントは、package-info.javaを作成することです。上記クラスを配置してあるパッケージに、package-info.javaを作成し、次のように@XmlSchemaアノテーションで、ネームスペースを指定してあげます。

package-info.java

@XmlSchema(namespace = "http://www.example.org/person/", elementFormDefault = XmlNsForm.QUALIFIED)
package jp.co.tryjava.jaxb.schema;

import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;

変換処理は、ネームスペース付加しない場合と同じ処理です。下記の「期待値」に記述したように、personタグにxmlns属性が付加されるようになりました。

オブジェクト→XML変換処理

// 入力オブジェクト
PersonWithXmlns person = new PersonWithXmlns();
person.name = "山田 太郎";
person.age = 20;

// 出力先
StringWriter actual = new StringWriter();

// 実行
JAXB.marshal(person, actual);

// 期待値
// xmlns属性が付くのがポイント!
String expected = "" //
+ "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" //
+ "<person xmlns=\"http://www.example.org/person/\">\n" //
+ "    <name>山田 太郎</name>\n" //
+ "    <age>20</age>\n" //
+ "</person>\n" //
;

// 検証
assertThat(actual.toString(), is(expected));

XML→オブジェクト変換

XML→オブジェクトの変換処理は次のようになりました。xmlns属性が付加されたXMLを入力データとして渡しています。

XML→オブジェクト変換処理

// 入力XML
String input = "" //
+ "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" //
+ "<person xmlns=\"http://www.example.org/person/\">\n" //
+ "    <name>山田 太郎</name>\n" //
+ "    <age>20</age>\n" //
+ "</person>\n" //
;
StringReader sr = new StringReader(input);

// 実行
PersonWithXmlns actual = JAXB.unmarshal(sr, PersonWithXmlns.class);

// 検証
assertThat(actual.name, is("山田 太郎"));
assertThat(actual.age, is(20));

XMLスキーマ検証

オブジェクト→XML変換

まずは、次のようなXMLスキーマファイルを用意しました。

※ここで「elementFormDefault="qualified"」が無いと、「org.xml.sax.SAXParseException; lineNumber: 0; columnNumber: 0; cvc-complex-type.2.4.a: 要素'name'で始まる無効なコンテンツが見つかりました。'{name}'のいずれかが必要です。」というエラーが発生してしまいました。

person.xsd

<?xml version="1.0" encoding="UTF-8"?>
<schema
  xmlns="http://www.w3.org/2001/XMLSchema"
  xmlns:tns="http://www.example.org/person/"
  targetNamespace="http://www.example.org/person/"
  elementFormDefault="qualified">

  <element name="person" type="tns:personType"></element>
  <complexType name="personType">
    <sequence>
      <element name="name" type="string"></element>
      <element name="age" type="int"></element>
    </sequence>
  </complexType>

</schema>

オブジェクト→XMLの変換処理は、次のようになりました。JavaオブジェクトとXMLスキーマの整合性が取れていない場合は、MarshalExceptionが発生しました。

オブジェクト→XML変換処理(XMLスキーマ検証あり)

// 入力オブジェクト
PersonWithXmlns person = new PersonWithXmlns();
person.name = "山田 太郎";
person.age = 20;

// 出力先
StringWriter actual = new StringWriter();

// スキーマインスタンス生成
SchemaFactory schemaFactory = SchemaFactory
        .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema(getClass().getResource(
        "/jp/co/tryjava/jaxb/person.xsd"));

// Marshallerインスタンス生成
JAXBContext context = JAXBContext.newInstance(person.getClass());
Marshaller marshaller = context.createMarshaller();
marshaller.setSchema(schema);
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

// 実行
marshaller.marshal(person, actual);

// 期待値
String expected = "" //
+ "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" //
+ "<person xmlns=\"http://www.example.org/person/\">\n" //
+ "    <name>山田 太郎</name>\n" //
+ "    <age>20</age>\n" //
+ "</person>\n" //
;

// 検証
assertThat(actual.toString(), is(expected));

XML→オブジェクト変換

XML→オブジェクトの変換処理は、次のようになりました。JavaオブジェクトとXMLスキーマの整合性が取れていない場合は、UnmarshalExceptionが発生しました。

XML→オブジェクト変換処理(XMLスキーマ検証あり)

// 入力XML
String input = "" //
+ "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" //
+ "<person xmlns=\"http://www.example.org/person/\">\n" //
+ "    <name>山田 太郎</name>\n" //
+ "    <age>20</age>\n" //
+ "</person>\n" //
;
StringReader sr = new StringReader(input);

// スキーマインスタンス生成
SchemaFactory schemaFactory = SchemaFactory
        .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema(getClass().getResource(
        "/jp/co/tryjava/jaxb/person.xsd"));

// Unmarshallerインスタンス生成
JAXBContext context = JAXBContext
        .newInstance(PersonWithXmlns.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
unmarshaller.setSchema(schema);

// 実行
PersonWithXmlns actual = (PersonWithXmlns) unmarshaller
        .unmarshal(sr);

// 検証
assertThat(actual.name, is("山田 太郎"));
assertThat(actual.age, is(20));