Javaを用いたシステム開発において、XML(Extensible Markup Language)は設定ファイルの記述や外部システムとのデータ連携、Webサービスのメッセージフォーマットとして長年重宝されてきました。

JSONが普及した現在においても、レガシーシステムの保守や金融・公的機関の標準プロトコルとして、XMLの操作スキルはJavaエンジニアにとって必須のテクニックと言えます。

しかし、Javaには標準ライブラリから外部ライブラリまで、XMLを作成するための選択肢が豊富に存在するため、プロジェクトの要件に合わせた最適な手法を選ぶことが重要です。

本記事では、標準的なDOM(Document Object Model)から、オブジェクトマッピングを実現するJAXB、そして現代的な開発で多用されるJacksonまで、具体的なコード例を交えて徹底的に解説します。

JavaでXMLを作成する主要な手法

JavaでXMLを生成する方法は、大きく分けて「低レベルAPIによるツリー構築」「ストリーミング処理」「データバインディング」の3つのアプローチに分類されます。

それぞれの特性を理解することで、メモリ効率や実装の容易さを考慮した最適な選択が可能になります。

DOM(Document Object Model)

W3C標準に基づく、最も伝統的な手法です。

XML全体をメモリ上のツリー構造(Documentオブジェクト)として保持し、要素を動的に追加・編集します。

小規模なXML作成には直感的で便利ですが、データ量が増えるとメモリ消費が激しくなるという特性があります。

StAX(Streaming API for XML)

「書き込みイベント」を逐次発行してXMLを構築する手法です。

DOMと異なり、メモリ上にツリーを構築しないため、大規模なXMLファイルを高速かつ低メモリで生成するのに適しています。

ただし、構造が複雑になるとコードの可読性が下がる傾向にあります。

JAXB(Jakarta XML Binding)

JavaクラスとXMLをアノテーションで紐付け、オブジェクトをそのままXMLに変換(マーシャリング)する手法です。

現在のJava開発におけるデファクトスタンダードの一つです。

なお、Java 11以降、JAXBはJDKの標準から切り離され、現在はJakarta EEの一部として提供されている点に注意が必要です。

Jackson(XML Dataformat)

JSONライブラリとして有名なJacksonですが、拡張モジュールを使用することでXMLの読み書きも非常に強力にサポートします。

JAXBと同様の使い勝手で、モダンなSpring Bootアプリケーションなどで広く採用されています。

DOMを用いたXMLの作成方法

まずは、Java標準のAPIであるDOMを使用してXMLを作成する方法を見ていきましょう。

DOMはjavax.xml.parsers.DocumentBuilderを使用して、空のドキュメントから要素を構築していきます。

実装例:DOMによる注文情報の作成

以下のプログラムは、注文情報を管理するシンプルなXMLを生成する例です。

Java
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.StringWriter;

public class DomXmlCreator {
    public static void main(String[] args) {
        try {
            // DocumentBuilderの生成
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document doc = builder.newDocument();

            // ルート要素 <order> の作成
            Element rootElement = doc.createElement("order");
            rootElement.setAttribute("id", "ORD-1001");
            doc.appendChild(rootElement);

            // 子要素 <customer> の追加
            Element customer = doc.createElement("customer");
            customer.setTextContent("田中 太郎");
            rootElement.appendChild(customer);

            // 子要素 <item> の追加
            Element item = doc.createElement("item");
            Element itemName = doc.createElement("name");
            itemName.setTextContent("Javaプログラミング書籍");
            Element price = doc.createElement("price");
            price.setTextContent("3200");
            
            item.appendChild(itemName);
            item.appendChild(price);
            rootElement.appendChild(item);

            // Transformerを使用してXMLを文字列として出力
            TransformerFactory transformerFactory = TransformerFactory.newInstance();
            Transformer transformer = transformerFactory.newTransformer();
            
            // インデント設定
            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");

            StringWriter writer = new StringWriter();
            transformer.transform(new DOMSource(doc), new StreamResult(writer));

            // 結果の表示
            System.out.println(writer.toString());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
XML
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<order id="ORD-1001">
    <customer>田中 太郎</customer>
    <item>
        <name>Javaプログラミング書籍</name>
        <price>3200</price>
    </item>
</order>

DOMを使用するメリットと注意点

DOMの最大のメリットは、ノードの移動や削除が容易であることです。

しかし、コードを見てわかる通り、要素を一つずつ作成して親子関係を構築する必要があり、記述が冗長になりがちです。

また、大規模なデータを扱う場合、すべてのノードをヒープメモリに保持するため、OutOfMemoryErrorのリスクを考慮する必要があります。

JAXBによるオブジェクトマッピング

次に、より実務的なアプローチであるJAXB(Jakarta XML Binding)を解説します。

JAXBはJavaオブジェクトをそのままXMLに変換できるため、ビジネスロジックとXML生成ロジックを切り離すことができます。

依存関係の追加(Maven)

Java 11以降を使用している場合、Mavenの pom.xml に以下の依存関係を追加する必要があります。

XML
<dependency>
    <groupId>jakarta.xml.bind</groupId>
    <artifactId>jakarta.xml.bind-api</artifactId>
    <version>4.0.0</version>
</dependency>
<dependency>
    <groupId>com.sun.xml.bind</groupId>
    <artifactId>jaxb-impl</artifactId>
    <version>4.0.0</version>
    <scope>runtime</scope>
</dependency>

実装例:JAXBアノテーションを活用した変換

XMLの構造をクラスとして定義し、アノテーションを付与します。

Java
import jakarta.xml.bind.annotation.XmlAttribute;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlRootElement;
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.Marshaller;
import java.io.StringWriter;

// ルート要素の定義
@XmlRootElement(name = "order")
class Order {
    private String id;
    private String customer;
    private Item item;

    @XmlAttribute // 属性として出力
    public String getId() { return id; }
    public void setId(String id) { this.id = id; }

    @XmlElement // 要素として出力
    public String getCustomer() { return customer; }
    public void setCustomer(String customer) { this.customer = customer; }

    @XmlElement
    public Item getItem() { return item; }
    public void setItem(Item item) { this.item = item; }
}

class Item {
    private String name;
    private int price;

    @XmlElement
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }

    @XmlElement
    public int getPrice() { return price; }
    public void setPrice(int price) { this.price = price; }
}

public class JaxbXmlCreator {
    public static void main(String[] args) {
        try {
            // データの準備
            Order order = new Order();
            order.setId("ORD-2002");
            order.setCustomer("佐藤 花子");
            
            Item item = new Item();
            item.setName("ワイヤレスマウス");
            item.setPrice(4500);
            order.setItem(item);

            // JAXBContextの初期化
            JAXBContext context = JAXBContext.newInstance(Order.class);
            Marshaller marshaller = context.createMarshaller();

            // フォーマット出力の設定
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);

            // XMLの出力
            StringWriter writer = new StringWriter();
            marshaller.marshal(order, writer);

            System.out.println(writer.toString());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
XML
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<order id="ORD-2002">
    <customer>佐藤 花子</customer>
    <item>
        <name>ワイヤレスマウス</name>
        <price>4500</price>
    </item>
</order>

JAXBを利用することで、ソースコードの可読性が劇的に向上します。

開発者はXMLの構造を意識することなく、Javaオブジェクトの操作に集中できるのが大きなメリットです。

Jacksonを使用したモダンなXML作成

JSON処理のライブラリとして有名なJacksonですが、jackson-dataformat-xmlモジュールを使用することで、非常に簡潔にXMLを作成できます。

JAXBのようなアノテーションベースの設定も可能であり、Spring Frameworkとの親和性が非常に高いのが特徴です。

依存関係の追加(Maven)

XML
<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
    <version>2.15.2</version>
</dependency>

実装例:XmlMapperによるシリアライズ

Jacksonを使用する場合、XmlMapperクラスを使用します。

Java
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import com.fasterxml.jackson.databind.SerializationFeature;

@JacksonXmlRootElement(localName = "product")
class Product {
    @JacksonXmlProperty(isAttribute = true)
    private String code;
    
    @JacksonXmlProperty(localName = "productName")
    private String name;

    public Product(String code, String name) {
        this.code = code;
        this.name = name;
    }
    
    // Getter/Setterは省略
}

public class JacksonXmlCreator {
    public static void main(String[] args) {
        try {
            XmlMapper xmlMapper = new XmlMapper();
            // インデントを有効にする
            xmlMapper.enable(SerializationFeature.INDENT_OUTPUT);

            Product product = new Product("P-500", "ゲーミングキーボード");

            // オブジェクトをXML文字列に変換
            String xml = xmlMapper.writeValueAsString(product);
            
            System.out.println(xml);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
XML
<product code="P-500">
  <productName>ゲーミングキーボード</productName>
</product>

Jacksonは、JSONとXMLを共通のロジックで扱いたい場合に最適です。

APIのレスポンス形式をリクエストに応じて切り替えるような要件では、これ以上ない選択肢となるでしょう。

各手法の比較と使い分け

JavaにおけるXML作成手法は多岐にわたりますが、どれを選ぶべきかはプロジェクトの性質によります。

以下の表に各手法の特徴をまとめました。

手法メモリ消費実装の容易さ主な用途推奨されるケース
DOM高い低い動的な構造変更小規模かつ複雑な加工が必要な場合
StAX極めて低い大量データの生成数GBクラスの巨大なXML出力
JAXB高いオブジェクト連携標準的な業務システム・SOAP連携
Jackson高いモダンなWeb APIJSONとXMLを併用するプロジェクト

選択の指針

JAXB

標準重視の場合。

Java EE/Jakarta EEの仕様に則り、メンテナンス性を高めたい場合に最適です。

StAX

パフォーマンス重視の場合。

バッチ処理などで大量のデータをXML化する場合、StAX一択となります。

Jackson

利便性重視の場合。

すでにプロジェクトでJacksonをJSON用に使っているなら、学習コストを抑えつつ導入できます。

XML作成における高度なテクニック

単にXMLを出力するだけでなく、実務では「名前空間(Namespace)」の扱いや「セキュリティ対策」も重要になります。

名前空間の付与

XMLの要素名が他のシステムと衝突しないよう、名前空間を使用することが一般的です。

JAXBでは package-info.java またはアノテーションの namespace 属性で定義します。

Java
@XmlRootElement(name = "order", namespace = "http://www.example.com/schema/order")

XML外部実体参照(XXE)攻撃への対策

XMLを「作成」する際には直接的な脅威にはなりにくいですが、作成したXMLを再度読み込む(パースする)ロジックが組み込まれている場合は、XXE攻撃への対策が不可欠です。

DOMやStAXを使用する際は、外部エンティティの解決を無効化する設定を必ず行いましょう。

効率的なファイル出力

文字列としてXMLを生成してメモリに溜めるのではなく、FileOutputStreamを直接 TransformerMarshaller に渡すことで、メモリ消費をさらに抑えることができます。

Java
// ファイルへ直接書き込む例
marshaller.marshal(order, new File("output.xml"));

まとめ

JavaでXMLを作成する手法は、歴史のあるDOMから現代的なJacksonまで多様な進化を遂げてきました。

  • DOMは、XMLの構造を自在に操る必要がある小規模な処理に向いています。
  • JAXBは、JavaクラスとXMLを直感的にマッピングでき、業務アプリケーションの標準的な選択肢です。
  • Jacksonは、JSONとの親和性が高く、Web開発における生産性を最大化します。
  • StAXは、パフォーマンスが最優先される大規模データ処理において威力を発揮します。

現代のJava開発においては、まずはJAXBまたはJacksonによるデータバインディングを検討し、それらが要件に合わない(非常に大きなファイルを扱う、あるいは非常に特殊な構造を持つ)場合にDOMやStAXを選択するのが、効率的でミスの少ない設計アプローチと言えるでしょう。

それぞれのライブラリの特性を正しく理解し、プロジェクトにとって最適なXML生成手法を選択してください。