Java : DocumentBuilderFactory (XML) - API使用例
DocumentBuilderFactory (Java SE 17 & JDK 17) の使用例まとめです。
だいたいのメソッドを網羅済みです。
API仕様のおともにどうぞ。
概要
DocumentBuilderFactoryは、XMLをDOMで操作する起点となるクラスです。
XML文字列からDOMを構築し、各要素にアクセスする一連の流れは次のようになります。
final var xml = """
<root>
<child-a>AAA</child-a>
<child-b>BBB</child-b>
</root>
""";
final var factory = DocumentBuilderFactory.newInstance();
final var builder = factory.newDocumentBuilder();
final var document = builder.parse(new ByteArrayInputStream(xml.getBytes()));
final var childA = document.getElementsByTagName("child-a").item(0);
System.out.println(childA); // [child-a: null]
System.out.println(childA.getTextContent()); // AAA
final var childB = document.getElementsByTagName("child-b").item(0);
System.out.println(childB); // [child-b: null]
System.out.println(childB.getTextContent()); // BBB
関連記事:XML (DOM) の基本
また、XMLの処理には、
- XML外部エンティティ(XXE)インジェクション攻撃
- 指数関数的エンティティ展開攻撃
といった潜在的な攻撃が存在します。
上記の公式ドキュメントでは、XML処理のセキュリティについて詳しく解説されています。
もし信頼していないXMLを読み込む必要がある場合は、上記ドキュメントも合わせてご確認ください。
コンストラクタ
DocumentBuilderFactory ()
protectedです。
独自にサブクラスを作ることは少ないと思いますので、コード例は割愛します。
メソッド
abstract Object getAttribute (String name)
final var dtdFile = Path.of("R:", "java-work", "sample.dtd");
System.out.println(dtdFile); // R:\java-work\sample.dtd
Files.writeString(dtdFile, """
<!ENTITY aaa "bbb">
""");
final var xml = """
<!DOCTYPE root SYSTEM "file:///R:/java-work/sample.dtd">
<root>&aaa;</root>
""";
final var factory = DocumentBuilderFactory.newInstance();
{
// すべての外部DTDの読み込みを許可します。
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "all");
final var ret = factory.getAttribute(XMLConstants.ACCESS_EXTERNAL_DTD);
System.out.println(ret); // "all"
final var builder = factory.newDocumentBuilder();
final var document = builder.parse(new ByteArrayInputStream(xml.getBytes()));
final var root = document.getDocumentElement();
System.out.println(root); // [root: null]
System.out.println(root.getTextContent()); // bbb
}
{
// すべての外部DTDの読み込みを拒否します。
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
final var ret = factory.getAttribute(XMLConstants.ACCESS_EXTERNAL_DTD);
System.out.println(ret); // ""
final var builder = factory.newDocumentBuilder();
try {
final var document = builder.parse(new ByteArrayInputStream(xml.getBytes()));
} catch (SAXException e) {
System.out.println(e);
}
// 結果
// ↓
//org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 57;
// 外部DTD: accessExternalDTDプロパティで設定された制限により'file'アクセスが
// 許可されていないため、外部DTD 'sample.dtd'の読取りに失敗しました。
}
abstract boolean getFeature (String name)
// 指数関数的エンティティ展開攻撃の例です。
final var xml = """
<!DOCTYPE root[
<!ENTITY x100 "X">
<!ENTITY x99 "&x100;&x100;">
<!ENTITY x98 "&x99;&x99;">
...
省略
...
<!ENTITY x3 "&x4;&x4;">
<!ENTITY x2 "&x3;&x3;">
<!ENTITY x1 "&x2;&x2;">
]>
<root>&x1;</root>
""";
final var factory = DocumentBuilderFactory.newInstance();
{
// セキュア処理機能(FSP)はデフォルトでtrueです。
final var ret = factory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING);
System.out.println(ret); // true
final var builder = factory.newDocumentBuilder();
try {
final var document = builder.parse(new ByteArrayInputStream(xml.getBytes()));
} catch (SAXException e) {
System.out.println(e);
}
// 結果
// ↓
//org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 1; JAXP00010001:
// パーサーによって、このドキュメント内で"64000"を超えるエンティティ拡張が検出されました。
// これは、JDKによる制限です。
}
// FSPをfalseにします。
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, false);
{
final var ret = factory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING);
System.out.println(ret); // false
final var builder = factory.newDocumentBuilder();
// ※注意:エンティティは指数関数的に増えているのでパースに非常に時間がかかります。
//final var document = builder.parse(new ByteArrayInputStream(xml.getBytes()));
}
Schema getSchema ()
final var xsd = """
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="root" type="xsd:string"/>
</xsd:schema>
""";
final var schemaFactory = SchemaFactory.newDefaultInstance();
final var schema = schemaFactory.newSchema(
new StreamSource(new ByteArrayInputStream(xsd.getBytes())));
final var factory = DocumentBuilderFactory.newInstance();
System.out.println(factory.getSchema()); // null
factory.setSchema(schema);
System.out.println(factory.getSchema().equals(schema)); // true
final var errorHandler = new DefaultHandler() {
@Override
public void error(SAXParseException e) {
System.out.println("-- ErrorHandler error --");
System.out.println(e);
}
};
{
// スキーマで指定した文書構造と一致する例
final var xml = """
<root>abcd</root>
""";
final var builder = factory.newDocumentBuilder();
builder.setErrorHandler(errorHandler);
final var document = builder.parse(new ByteArrayInputStream(xml.getBytes()));
final var root = document.getDocumentElement();
System.out.println(root); // [root: null]
System.out.println(root.getTextContent()); // abcd
}
{
// スキーマで指定した文書構造と一致しない例
final var xml = """
<root><child>abcd</child></root>
""";
final var builder = factory.newDocumentBuilder();
builder.setErrorHandler(errorHandler);
final var document = builder.parse(new ByteArrayInputStream(xml.getBytes()));
// 結果
// ↓
//-- ErrorHandler error --
//org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 33; cvc-type.3.1.2:
// 要素'root'は単純型であるため、要素情報アイテム[children]を含めることはできません。
}
boolean isCoalescing ()
final var xml = """
<root>aaa<![CDATA[<&>]]></root>
""";
final var factory = DocumentBuilderFactory.newInstance();
{
final var ret = factory.isCoalescing();
System.out.println(ret); // false
final var builder = factory.newDocumentBuilder();
final var document = builder.parse(new ByteArrayInputStream(xml.getBytes()));
final var root = document.getDocumentElement();
System.out.println(root); // [root: null]
final var nodes = root.getChildNodes();
System.out.println("-- nodes --");
for (int i = 0; i < nodes.getLength(); i++) {
System.out.println(nodes.item(i));
}
// 結果
// ↓
//-- nodes --
//[#text: aaa]
//[#cdata-section: <&>]
}
factory.setCoalescing(true);
{
final var ret = factory.isCoalescing();
System.out.println(ret); // true
final var builder = factory.newDocumentBuilder();
final var document = builder.parse(new ByteArrayInputStream(xml.getBytes()));
final var root = document.getDocumentElement();
System.out.println(root); // [root: null]
final var nodes = root.getChildNodes();
System.out.println("-- nodes --");
for (int i = 0; i < nodes.getLength(); i++) {
System.out.println(nodes.item(i));
}
// 結果
// ↓
//-- nodes --
//[#text: aaa<&>]
}
boolean isExpandEntityReferences ()
final var xml = """
<!DOCTYPE root [
<!ENTITY aaa "bbb">
]>
<root>&aaa;</root>
""";
final var factory = DocumentBuilderFactory.newInstance();
{
final var ret = factory.isExpandEntityReferences();
System.out.println(ret); // true
final var builder = factory.newDocumentBuilder();
final var document = builder.parse(new ByteArrayInputStream(xml.getBytes()));
final var root = document.getDocumentElement();
System.out.println(root); // [root: null]
final var child = root.getFirstChild();
System.out.println(child); // [#text: bbb]
}
factory.setExpandEntityReferences(false);
{
final var ret = factory.isExpandEntityReferences();
System.out.println(ret); // false
final var builder = factory.newDocumentBuilder();
final var document = builder.parse(new ByteArrayInputStream(xml.getBytes()));
final var root = document.getDocumentElement();
System.out.println(root); // [root: null]
if (root.getFirstChild() instanceof EntityReference entityReference) {
System.out.println(entityReference); // [aaa: null]
}
}
boolean isIgnoringComments ()
final var xml = """
<root>aaa<!--bbb--></root>
""";
final var factory = DocumentBuilderFactory.newInstance();
{
final var ret = factory.isIgnoringComments();
System.out.println(ret); // false
final var builder = factory.newDocumentBuilder();
final var document = builder.parse(new ByteArrayInputStream(xml.getBytes()));
final var root = document.getDocumentElement();
System.out.println(root); // [root: null]
final var nodes = root.getChildNodes();
System.out.println("-- nodes --");
for (int i = 0; i < nodes.getLength(); i++) {
System.out.println(nodes.item(i));
}
// 結果
// ↓
//-- nodes --
//[#text: aaa]
//[#comment: bbb]
}
factory.setIgnoringComments(true);
{
final var ret = factory.isIgnoringComments();
System.out.println(ret); // true
final var builder = factory.newDocumentBuilder();
final var document = builder.parse(new ByteArrayInputStream(xml.getBytes()));
final var root = document.getDocumentElement();
System.out.println(root); // [root: null]
final var nodes = root.getChildNodes();
System.out.println("-- nodes --");
for (int i = 0; i < nodes.getLength(); i++) {
System.out.println(nodes.item(i));
}
// 結果
// ↓
//-- nodes --
//[#text: aaa]
}
boolean isIgnoringElementContentWhitespace ()
無視される空白は、テキスト(#PCDATA)を持たないことを宣言した要素が対象となります。
詳細は setIgnoringElementContentWhitespace(boolean whitespace) のAPI仕様もご確認ください。
// child-a, child-b要素は半角スペースを持ちます。
final var xml = """
<!DOCTYPE root [
<!ELEMENT child-a (dummy?)>
<!ELEMENT child-b (#PCDATA)>
]>
<root>
<child-a> </child-a>
<child-b> </child-b>
</root>
""";
final var factory = DocumentBuilderFactory.newInstance();
{
final var ret = factory.isIgnoringElementContentWhitespace();
System.out.println(ret); // false
final var builder = factory.newDocumentBuilder();
final var document = builder.parse(new ByteArrayInputStream(xml.getBytes()));
final var childA = document.getElementsByTagName("child-a").item(0);
System.out.println(childA); // [child-a: null]
final var childB = document.getElementsByTagName("child-b").item(0);
System.out.println(childB); // [child-b: null]
System.out.println(childA.getFirstChild()); // [#text: ]
System.out.println(childB.getFirstChild()); // [#text: ]
}
factory.setIgnoringElementContentWhitespace(true);
{
final var ret = factory.isIgnoringElementContentWhitespace();
System.out.println(ret); // true
final var builder = factory.newDocumentBuilder();
final var document = builder.parse(new ByteArrayInputStream(xml.getBytes()));
final var childA = document.getElementsByTagName("child-a").item(0);
System.out.println(childA); // [child-a: null]
final var childB = document.getElementsByTagName("child-b").item(0);
System.out.println(childB); // [child-b: null]
System.out.println(childA.getFirstChild()); // null
System.out.println(childB.getFirstChild()); // [#text: ]
}
boolean isNamespaceAware ()
final var xml = """
<ns:root xmlns:ns="sample">
<ns:child/>
</ns:root>
""";
final var factory = DocumentBuilderFactory.newInstance();
{
final var ret = factory.isNamespaceAware();
System.out.println(ret); // false
final var builder = factory.newDocumentBuilder();
final var document = builder.parse(new ByteArrayInputStream(xml.getBytes()));
final var child = document.getElementsByTagNameNS("sample", "child").item(0);
System.out.println(child); // null
}
factory.setNamespaceAware(true);
{
final var ret = factory.isNamespaceAware();
System.out.println(ret); // true
final var builder = factory.newDocumentBuilder();
final var document = builder.parse(new ByteArrayInputStream(xml.getBytes()));
final var child = document.getElementsByTagNameNS("sample", "child").item(0);
System.out.println(child); // [ns:child: null]
}
boolean isValidating ()
// 意図的に文書構造と一致しないXMLにしています。
final var xml = """
<!DOCTYPE root [
<!ELEMENT root (child-a)>
]>
<root><child-z/></root>
""";
final var factory = DocumentBuilderFactory.newInstance();
final var errorHandler = new DefaultHandler() {
@Override
public void error(SAXParseException e) {
System.out.println("-- ErrorHandler error --");
System.out.println(e);
}
};
{
final var ret = factory.isValidating();
System.out.println(ret); // false
final var builder = factory.newDocumentBuilder();
builder.setErrorHandler(errorHandler);
final var document = builder.parse(new ByteArrayInputStream(xml.getBytes()));
final var childZ = document.getElementsByTagName("child-z").item(0);
System.out.println(childZ); // [child-z: null]
}
factory.setValidating(true);
{
final var ret = factory.isValidating();
System.out.println(ret); // true
final var builder = factory.newDocumentBuilder();
builder.setErrorHandler(errorHandler);
final var document = builder.parse(new ByteArrayInputStream(xml.getBytes()));
// 結果
// ↓
//-- ErrorHandler error --
//org.xml.sax.SAXParseException; lineNumber: 4; columnNumber: 17;
// 要素タイプ"child-z"を宣言する必要があります。
//-- ErrorHandler error --
//org.xml.sax.SAXParseException; lineNumber: 4; columnNumber: 24;
// 要素タイプ"root"のコンテンツは"(child-a)"と一致する必要があります。
}
boolean isXIncludeAware ()
final var sampleFile = Path.of("R:", "java-work", "sample.xml");
System.out.println(sampleFile); // R:\java-work\sample.xml
Files.writeString(sampleFile, """
<child>abcd</child>
""");
final var xml = """
<root xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="file:///R:/java-work/sample.xml" parse="xml" />
</root>
""";
// XIncludeを使うには、名前空間も有効にする必要があります。
final var factory = DocumentBuilderFactory.newNSInstance();
{
final var ret = factory.isXIncludeAware();
System.out.println(ret); // false
final var builder = factory.newDocumentBuilder();
final var document = builder.parse(new ByteArrayInputStream(xml.getBytes()));
final var nodes = document.getElementsByTagName("child");
System.out.println(nodes.getLength()); // 0
}
factory.setXIncludeAware(true);
{
final var ret = factory.isXIncludeAware();
System.out.println(ret); // true
final var builder = factory.newDocumentBuilder();
final var document = builder.parse(new ByteArrayInputStream(xml.getBytes()));
final var child = document.getElementsByTagName("child").item(0);
System.out.println(child); // [child: null]
System.out.println(child.getTextContent()); // abcd
}
static DocumentBuilderFactory newDefaultInstance ()
newInstanceとほぼ同じです。
API使用例はそちらをご参照ください。
補足:newInstanceとの違い
newInstance は、いろいろと DocumentBuilderFactory の実装を探して、なければ最後に組み込みシステムのデフォルト実装を使います。 newDefaultInstance は、組み込みシステムのデフォルト実装をすぐに使います。
よって、メソッドの呼び出し速度は newDefaultInstance のほうが速いです。
自分の環境(openjdk-17.0.2_windows)ではどちらも同じ実装を返しました。
final var defaultFactory = DocumentBuilderFactory.newDefaultInstance();
// com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl@7857fe2
System.out.println(defaultFactory);
final var factory = DocumentBuilderFactory.newInstance();
// com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl@5340477f
System.out.println(factory);
どちらを使うべきか?というのは正直、分かりません…
拡張性が高いのは newInstance のほうなので、個人的には newInstance でよいのではないかなと思います。
static DocumentBuilderFactory newDefaultNSInstance ()
isNamespaceAwareがtrueとなること以外はnewDefaultInstanceと同等です。
そちらのAPI使用例もご参照ください。
final var nsFactory = DocumentBuilderFactory.newDefaultNSInstance();
System.out.println(nsFactory.isNamespaceAware()); // true
final var factory = DocumentBuilderFactory.newDefaultInstance();
System.out.println(factory.isNamespaceAware()); // false
abstract DocumentBuilder newDocumentBuilder ()
このメソッドの使用例は、newInstance() にまとめて記載しました。
そちらのAPI使用例をご参照ください。
static DocumentBuilderFactory newInstance ()
final var xml = """
<root>
<child-a>AAA</child-a>
<child-b>BBB</child-b>
</root>
""";
final var factory = DocumentBuilderFactory.newInstance();
final var builder = factory.newDocumentBuilder();
final var document = builder.parse(new ByteArrayInputStream(xml.getBytes()));
final var childA = document.getElementsByTagName("child-a").item(0);
System.out.println(childA); // [child-a: null]
System.out.println(childA.getTextContent()); // AAA
final var childB = document.getElementsByTagName("child-b").item(0);
System.out.println(childB); // [child-b: null]
System.out.println(childB.getTextContent()); // BBB
static DocumentBuilderFactory newInstance (String factoryClassName, ClassLoader classLoader)
おそらくサードパーティ製のパーサを使うときのためのAPIです。
本記事ではコード例は割愛します。
static DocumentBuilderFactory newNSInstance ()
isNamespaceAwareがtrueとなること以外はnewInstanceと同等です。
そちらのAPI使用例もご参照ください。
final var nsFactory = DocumentBuilderFactory.newNSInstance();
System.out.println(nsFactory.isNamespaceAware()); // true
final var factory = DocumentBuilderFactory.newInstance();
System.out.println(factory.isNamespaceAware()); // false
static DocumentBuilderFactory newNSInstance (String factoryClassName, ClassLoader classLoader)
おそらくサードパーティ製のパーサを使うときのためのAPIです。
本記事ではコード例は割愛します。
abstract void setAttribute (String name, Object value)
このメソッドの使用例は、getAttribute(String name) にまとめて記載しました。
そちらのAPI使用例をご参照ください。
void setCoalescing (boolean coalescing)
このメソッドの使用例は、isCoalescing() にまとめて記載しました。
そちらのAPI使用例をご参照ください。
void setExpandEntityReferences (boolean expandEntityRef)
このメソッドの使用例は、isExpandEntityReferences() にまとめて記載しました。
そちらのAPI使用例をご参照ください。
abstract void setFeature (String name, boolean value)
このメソッドの使用例は、getFeature(String name) にまとめて記載しました。
そちらのAPI使用例をご参照ください。
void setIgnoringComments (boolean ignoreComments)
このメソッドの使用例は、isIgnoringComments() にまとめて記載しました。
そちらのAPI使用例をご参照ください。
void setIgnoringElementContentWhitespace (boolean whitespace)
このメソッドの使用例は、isIgnoringElementContentWhitespace() にまとめて記載しました。
そちらのAPI使用例をご参照ください。
void setNamespaceAware (boolean awareness)
このメソッドの使用例は、isNamespaceAware() にまとめて記載しました。
そちらのAPI使用例をご参照ください。
void setSchema (Schema schema)
このメソッドの使用例は、getSchema() にまとめて記載しました。
そちらのAPI使用例をご参照ください。
void setValidating (boolean validating)
このメソッドの使用例は、isValidating() にまとめて記載しました。
そちらのAPI使用例をご参照ください。
void setXIncludeAware (boolean state)
このメソッドの使用例は、isXIncludeAware() にまとめて記載しました。
そちらのAPI使用例をご参照ください。
関連記事
公式ドキュメント
XML