1. 概要

XML ファイルを手動で読み取る必要がある場合、通常は、きれいに印刷された形式でコンテンツを読み取ります。 多くのテキストエディタまたはIDEは、XMLドキュメントを再フォーマットできます。 Linuxで作業している場合は、コマンドラインからXMLファイルをきれいに印刷できます。

ただし、Javaプログラムで生のXML文字列をきれいに印刷された形式に変換する必要がある場合があります。 たとえば、見やすくするために、きれいに印刷されたXMLドキュメントをユーザーインターフェイスに表示したい場合があります。

このチュートリアルでは、JavaでXMLをきれいに印刷する方法を探ります。

2. 問題の紹介

簡単にするために、フォーマットされていないemails.xmlファイルを入力として使用します。

<emails> <email> <from>Kai</from> <to>Amanda</to> <time>2018-03-05</time>
<subject>I am flying to you</subject></email> <email>
<from>Jerry</from> <to>Tom</to> <time>1992-08-08</time> <subject>Hey Tom, catch me if you can!</subject>
</email> </emails>

ご覧のとおり、emails.xmlファイルは整形式です。 ただし、フォーマットが乱雑であるため、読みやすくありません。

私たちの目標は、この醜い生のXML文字列をきれいにフォーマットされた文字列に変換するメソッドを作成することです。

さらに、2つの一般的な出力プロパティのカスタマイズについて説明します。インデントサイズ( integer )とXML宣言の抑制( boolean )です。

indent-sizeプロパティは非常に単純です。これは、インデントするスペースの数(レベルごと)です。 一方、XML宣言の抑制オプションは、生成されたXMLにXML宣言タグを含めるかどうかを決定します。 典型的なXML宣言は次のようになります。

<?xml version="1.0" encoding="UTF-8"?>

このチュートリアルでは、標準のJava APIを使用したソリューションと、外部ライブラリを使用した別のアプローチについて説明します。

次に、それらの動作を見てみましょう。

3. Transformerクラスを使用したXMLのきれいな印刷

Java APIは、XML変換を行うためのTransformerクラスを提供します。

3.1. デフォルトのTransformerを使用する

まず、Transformerクラスを使用したプリティプリントソリューションを見てみましょう。

public static String prettyPrintByTransformer(String xmlString, int indent, boolean ignoreDeclaration) {

    try {
        InputSource src = new InputSource(new StringReader(xmlString));
        Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(src);

        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        transformerFactory.setAttribute("indent-number", indent);
        Transformer transformer = transformerFactory.newTransformer();
        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
        transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, ignoreDeclaration ? "yes" : "no");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");

        Writer out = new StringWriter();
        transformer.transform(new DOMSource(document), new StreamResult(out));
        return out.toString();
    } catch (Exception e) {
        throw new RuntimeException("Error occurs when pretty-printing xml:\n" + xmlString, e);
    }
}

それでは、メソッドをすばやく見ていき、どのように機能するかを理解しましょう。

  • まず、生のXML文字列を解析し、Documentオブジェクトを取得します。
  • 次に、 TransformationrFactory インスタンスを取得し、必要なインデントサイズ属性を設定します。
  • 次に、構成済みのtransformerFactoryオブジェクトからデフォルトのトランスフォーマーインスタンスを取得できます。
  • transformer オブジェクトは、さまざまな出力プロパティをサポートしています。 宣言をスキップするかどうかを決定するために、OutputKeys.OMIT_XML_DECLARATION属性を設定します。
  • きれいにフォーマットされたStringオブジェクトが必要なので、最後に、解析されたXMLドキュメントStringWritertransform()します。変換されたStringを返します。

上記のメソッドでTransformerFactoryオブジェクトにインデントサイズを設定しました。 または、トランスフォーマーインスタンスでインデント量プロパティを定義することもできます

transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", String.valueOf(indent));

次に、メソッドが期待どおりに機能するかどうかをテストしましょう。

3.2. メソッドのテスト

JavaプロジェクトはMavenプロジェクトであり、emails.xmlsrc/ main / resources / xml /email.xmlの下に配置しました。 readFromInputStream メソッドを作成して、入力ファイルをStringとして読み取ります。 ただし、ここでのトピックとはあまり関係がないため、このメソッドの詳細については説明しません。 indent-size = 2を設定し、結果のXML宣言をスキップするとします。

public static void main(String[] args) throws IOException {
    InputStream inputStream = XmlPrettyPrinter.class.getResourceAsStream("/xml/emails.xml");
    String xmlString = readFromInputStream(inputStream);
    System.out.println("Pretty printing by Transformer");
    System.out.println("=============================================");
    System.out.println(prettyPrintByTransformer(xmlString, 2, true));
}

main メソッドが示すように、入力ファイルを String として読み取り、 prettyPrintByTransformer メソッドを呼び出して、きれいに印刷されたXML String[を取得します。 X176X]。

次に、 Java8を使用してmainメソッドを実行してみましょう。

Pretty printing by Transformer
=============================================
<emails>
  <email>
    <from>Kai</from>
    <to>Amanda</to>
    <time>2018-03-05</time>
    <subject>I am flying to you</subject>
  </email>
  <email>
    <from>Jerry</from>
    <to>Tom</to>
    <time>1992-08-08</time>
    <subject>Hey Tom, catch me if you can!</subject>
  </email>
</emails>

上記の出力が示すように、私たちの方法は期待どおりに機能します。

ただし、Java 9以降のバージョンでもう一度テストすると、異なる出力が表示される場合があります。

次に、 Java9で実行した場合に何が生成されるかを見てみましょう。

Pretty printing by Transformer
=============================================
<emails>
   
  <email>
     
    <from>Kai</from>
     
    <to>Amanda</to>
     
    <time>2018-03-05</time>
    
    <subject>I am flying to you</subject>
  </email>
   
  <email>
    
    <from>Jerry</from>
     
    <to>Tom</to>
     
    <time>1992-08-08</time>
     
    <subject>Hey Tom, catch me if you can!</subject>
    
  </email>
   
</emails>

=============================================

上記の出力でわかるように、出力に予期しない空の行があります。

これは、生の入力に要素間に空白が含まれているためです。次に例を示します。

<emails> <email> <from>Kai</from> ...

Java 9の時点で、Transformerクラスのプリティプリント機能は実際のフォーマットを定義していません。 したがって、空白のみのノードも出力されます。 これはこれで議論されています JDKバグチケット 。 また、 Java 9のリリースノートでは、xml/jaxpセクションでこれについて説明しています。

さまざまなJavaバージョンでpretty-printメソッドが常に同じ形式を生成するようにする場合は、スタイルシートファイルを提供する必要があります。

次に、それを実現するための単純なxslファイルを作成しましょう。

3.3. XSLTファイルの提供

まず、 prettyprint.xsl ファイルを作成して、出力形式を定義しましょう。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:strip-space elements="*"/>
    <xsl:output method="xml" encoding="UTF-8"/>

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

ご覧のとおり、 prettyprint.xsl ファイル、 私たちは使用しました空白のみのノードを削除して、出力に表示されないようにする要素

次に、メソッドに小さな変更を加える必要があります。 デフォルトのトランスフォーマーは使用しなくなります。 代わりに、XSLTドキュメントを使用してTransformerオブジェクトを作成します。

Transformer transformer = transformerFactory.newTransformer(new StreamSource(new StringReader(readPrettyPrintXslt())));

ここで、 readPrettyPrintXslt()メソッドはprettyprint.xslコンテンツを読み取ります。

ここで、Java8とJava9でメソッドをテストすると、どちらも同じ出力を生成します。

Pretty printing by Transformer
=============================================
<emails>
  <email>
    <from>Kai</from>
    <to>Amanda</to>
    <time>2018-03-05</time>
    <subject>I am flying to you</subject>
  </email>
...
</emails>

標準のJavaAPIで問題を解決しました。 次に、外部ライブラリを使用してemails.xmlをきれいに印刷しましょう。

4. Dom4jライブラリを使用したXMLのきれいな印刷

Dom4jは人気のあるXMLライブラリです。 これにより、XMLドキュメントを簡単にきれいに印刷できます。

まず、Dom4j依存関係をpom.xmlに追加しましょう。

<dependency>
    <groupId>org.dom4j</groupId>
    <artifactId>dom4j</artifactId>
    <version>2.1.3</version>
</dependency>

例として2.1.3バージョンを使用しました。 最新バージョンはMavenCentralリポジトリーにあります。

次に、Dom4jライブラリを使用してXMLをきれいに印刷する方法を見てみましょう。

public static String prettyPrintByDom4j(String xmlString, int indent, boolean skipDeclaration) {
    try {
        OutputFormat format = OutputFormat.createPrettyPrint();
        format.setIndentSize(indent);
        format.setSuppressDeclaration(skipDeclaration);
        format.setEncoding("UTF-8");

        org.dom4j.Document document = DocumentHelper.parseText(xmlString);
        StringWriter sw = new StringWriter();
        XMLWriter writer = new XMLWriter(sw, format);
        writer.write(document);
        return sw.toString();
    } catch (Exception e) {
        throw new RuntimeException("Error occurs when pretty-printing xml:\n" + xmlString, e);
    }
}

D0m4jのOutputFormatクラスは、事前定義されたプリティプリントのOutputFormatオブジェクトを作成するためのcreatePrettyPrintメソッドを提供しています。 上記の方法が示すように、デフォルトのプリティプリント形式にいくつかのカスタマイズを追加できます。 この場合、インデントサイズを設定し、結果に宣言を含めるかどうかを決定します。

次に、生のXML文字列を解析し、準備されたOutputFormatインスタンスを使用してXMLWritterオブジェクトを作成します。

最後に、 XMLWriter オブジェクトは、解析されたXMLドキュメントを必要な形式で書き込みます。

次に、emails.xmlファイルをきれいに印刷できるかどうかをテストしましょう。 今回は、宣言を含め、結果にインデントサイズを8にしたいとします。

System.out.println("Pretty printing by Dom4j");
System.out.println("=============================================");
System.out.println(prettyPrintByDom4j(xmlString, 8, false));

メソッドを実行すると、次の出力が表示されます。

Pretty printing by Dom4j
=============================================
<?xml version="1.0" encoding="UTF-8"?>

<emails> 
        <email> 
                <from>Kai</from>  
                <to>Amanda</to>  
                <time>2018-03-05</time>  
                <subject>I am flying to you</subject>
        </email>  
        <email> 
                <from>Jerry</from>  
                <to>Tom</to>  
                <time>1992-08-08</time>  
                <subject>Hey Tom, catch me if you can!</subject> 
        </email> 
</emails>

上記の出力が示すように、この方法は問題を解決しました。

5. 結論

この記事では、JavaでXMLファイルをきれいに印刷するための2つのアプローチについて説明しました。

標準のJavaAPIを使用してXMLをきれいに印刷できます。 ただし、 Transformer オブジェクトは、Javaのバージョンによって異なる結果を生成する可能性があることに注意する必要があります。 解決策は、XSLTファイルを提供することです。

または、Dom4jライブラリで問題を簡単に解決できます。

いつものように、コードのフルバージョンはGitHubから入手できます。