XML生成は「機械にも人にも読める構造を作る」技
JSON が主流になった今でも、業務の世界では XML が普通に生きています。
古い外部システム連携、バッチの入出力、設定ファイル、帳票レイアウト定義など、「まだまだ XML 前提」の世界は多いです。
XML生成で大事なのは、「見た目だけそれっぽい文字列」を作るのではなく、
タグの対応・エスケープ・インデントなどをきちんと守った「構造として正しい XML」を、毎回同じルールで出せるようにすることです。
まずは「XMLの最低限のルール」を押さえる
タグの対応とエスケープが最重要
XML はざっくり言うと「タグで囲まれた階層構造のテキスト」です。
<user>
<id>u-001</id>
<name>山田太郎</name>
</user>
ここで最低限守らないといけないルールは次のようなものです。
タグは必ず開始タグと終了タグがペアになる(<user> と </user>)。< > & " ' などの記号は、内容として使うときにエスケープが必要(< > & など)。
ルート要素は一つだけ(XML 全体を包む一番外側のタグ)。
これを手書きの文字列連結でやろうとすると、すぐに壊れます。
だからこそ、「XML を組み立てる仕組み」に任せるか、「エスケープと構造」をユーティリティに閉じ込めるのが大事になります。
業務で現実的な選択肢:ライブラリを使うか、最小限だけ自前でやるか
JAXB や Jackson XML などの「オブジェクト→XML」ライブラリ
本格的に XML を扱うなら、JAXB(Jakarta XML Binding)や Jackson の XML モジュールのような、
「Java オブジェクトを XML に変換してくれるライブラリ」を使うのが王道です。
ただ、ここでは「まずは仕組みを理解したい」「小さなユーティリティから始めたい」という前提で、
標準 API と軽いユーティリティで XML を生成する流れを見ていきます。
DOM を使った「ちゃんとした XML生成」の最小例
DocumentBuilder でツリーを作ってから文字列にする
標準の org.w3c.dom と javax.xml.transform を使うと、
「XML のツリー(DOM)を組み立ててから、文字列に変換する」という流れで、安全に XML を生成できます。
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 final class Xmls {
private Xmls() {}
public static String buildUserXml(String id, String name) {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.newDocument();
Element root = doc.createElement("user");
doc.appendChild(root);
Element idElem = doc.createElement("id");
idElem.setTextContent(id);
root.appendChild(idElem);
Element nameElem = doc.createElement("name");
nameElem.setTextContent(name);
root.appendChild(nameElem);
return toString(doc);
} catch (Exception e) {
throw new IllegalStateException("XML 生成に失敗しました", e);
}
}
private static String toString(Document doc) throws Exception {
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
StringWriter writer = new StringWriter();
transformer.transform(new DOMSource(doc), new StreamResult(writer));
return writer.toString();
}
}
Java使い方はこうです。
String xml = Xmls.buildUserXml("u-001", "山田 & 太郎 <営業>");
System.out.println(xml);
Java出力イメージは次のようになります。
<?xml version="1.0" encoding="UTF-8"?>
<user>
<id>u-001</id>
<name>山田 & 太郎 <営業></name>
</user>
ここで深掘りしたい重要ポイントは、「テキスト内容を setTextContent で設定すると、危ない文字を自動でエスケープしてくれる」ことです。& や < を自分で & や < に変換する必要はありません。
DOM の API に任せることで、「エスケープ漏れで XML が壊れる」という事故を防げます。
また、Transformer に INDENT を設定することで、インデント付きの読みやすい XML を出力しています。
人間が見るログやファイルではインデントあり、本番通信ではインデントなし、という切り替えもここでできます。
例題:業務エンティティから XML を生成する
POJO から「レポート用 XML」を作る
例えば、注文情報を表すクラスがあるとします。
public class Order {
private String id;
private String userId;
private int amount;
public Order(String id, String userId, int amount) {
this.id = id;
this.userId = userId;
this.amount = amount;
}
public String getId() { return id; }
public String getUserId() { return userId; }
public int getAmount() { return amount; }
}
Javaこの Order を XML に変換するユーティリティを作ります。
import org.w3c.dom.Document;
import org.w3c.dom.Element;
public final class OrderXmlBuilder {
private OrderXmlBuilder() {}
public static String toXml(Order order) {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.newDocument();
Element root = doc.createElement("order");
doc.appendChild(root);
appendTextElement(doc, root, "id", order.getId());
appendTextElement(doc, root, "userId", order.getUserId());
appendTextElement(doc, root, "amount", String.valueOf(order.getAmount()));
return Xmls.toString(doc); // 先ほどの toString を再利用
} catch (Exception e) {
throw new IllegalStateException("Order XML 生成に失敗しました", e);
}
}
private static void appendTextElement(Document doc, Element parent, String name, String value) {
Element elem = doc.createElement(name);
elem.setTextContent(value);
parent.appendChild(elem);
}
}
Java使い方はこうです。
Order order = new Order("o-001", "u-001", 5000);
String xml = OrderXmlBuilder.toXml(order);
System.out.println(xml);
Javaここでのポイントは、「XML の構造(タグ名・階層)を OrderXmlBuilder に閉じ込めている」ことです。
呼び出し側は「Order を XML にしたい」とだけ考えればよく、
「どのタグ名にするか」「どの順番で並べるか」「エスケープはどうするか」は、ビルダー側に任せられます。
こうしておくと、「タグ名を変えたい」「要素を追加したい」といった変更も、
OrderXmlBuilder だけを直せばよくなり、業務ロジックへの影響を最小限にできます。
文字列連結で「軽量XML」を作るときの最低限の防御
ログ用やごく簡単な構造なら、エスケープユーティリティを挟む
DOM を使うのは少し重い、ログ用に簡単な XML っぽいものを出したい、
という場面では、文字列連結で作ることもあります。
その場合でも、「テキスト内容のエスケープだけは絶対にユーティリティに任せる」ようにします。
public final class XmlEscape {
private XmlEscape() {}
public static String text(String value) {
if (value == null) {
return "";
}
String s = value;
s = s.replace("&", "&");
s = s.replace("<", "<");
s = s.replace(">", ">");
s = s.replace("\"", """);
s = s.replace("'", "'");
return s;
}
}
Javaこれを使って、簡易的な XML を作ります。
public final class SimpleXmlLog {
private SimpleXmlLog() {}
public static String userLogXml(String id, String name) {
return "<userLog>" +
"<id>" + XmlEscape.text(id) + "</id>" +
"<name>" + XmlEscape.text(name) + "</name>" +
"</userLog>";
}
}
Java使い方はこうです。
String xml = SimpleXmlLog.userLogXml("u-001", "山田 & 太郎 <営業>");
System.out.println(xml);
Java出力は次のようになります。
<userLog><id>u-001</id><name>山田 & 太郎 <営業></name></userLog>
ここで深掘りしたいのは、「“タグの構造は自分で書いてもいいが、テキストのエスケープは必ずユーティリティを通す”という線引き」です。
これを徹底するだけで、「& が原因で XML パーサがコケる」といった事故をかなり防げます。
XML生成で意識したい設計のポイント
ルート要素と名前空間をどう扱うか
業務の XML では、ルート要素や名前空間(xmlns="...")が仕様で決まっていることが多いです。
例えば、こんな感じです。
<order xmlns="http://example.com/schema/order">
...
</order>
DOM で名前空間付きの要素を作る場合は、createElementNS を使います。
String ns = "http://example.com/schema/order";
Element root = doc.createElementNS(ns, "order");
doc.appendChild(root);
Javaここでのポイントは、「名前空間やルート要素の扱いも、ビルダー側に閉じ込める」ことです。
呼び出し側が xmlns を意識しなくてよいようにしておくと、仕様変更にも強くなります。
インデントや XML宣言の有無を統一する
Transformer の設定次第で、XML 宣言(<?xml version="1.0" ...?>)を付けるかどうか、
インデントを付けるかどうかを制御できます。
これを各所でバラバラにすると、「この XML だけ宣言がない」「このファイルだけインデントがない」といった揺れが出ます。Xmls.toString のような共通ユーティリティで、「プロジェクトとしての標準」を一箇所に決めておくと、
どこで XML を出しても同じスタイルになります。
まとめ:XML生成ユーティリティで身につけたい感覚
XML生成は、「それっぽい文字列を組み立てる」のではなく、
「構造とエスケープを仕組みに任せて、毎回同じルールで安全に出す」ための技です。
押さえておきたい感覚は、まず「DOM などの API を使えば、テキストのエスケープを自分でやらなくてよくなる」こと。
次に、「業務エンティティごとに“XMLビルダー”を用意し、タグ構造や名前空間をそこに閉じ込める」こと。
そして、「どうしても文字列連結でやる場面でも、テキスト部分だけは必ずエスケープユーティリティを通す」という最低限の防御ラインを持つことです。
