5

いくつかの XML ドキュメントを並べ替えて正規化しようとしています。望ましい最終結果は次のとおりです。

  1. すべての要素の子はアルファベット順です
  2. すべての要素の属性はアルファベット順です
  3. コメントは削除されます
  4. すべての要素が適切な間隔で配置されています (つまり、「きれいな印刷」)。

1 を除くすべての目標を達成しました。

この回答をテンプレートとして使用しています。これが私がこれまでに持っているものです:

import javax.xml.transform.stream.StreamResult
import javax.xml.transform.stream.StreamSource
import javax.xml.transform.TransformerFactory
import org.apache.xml.security.c14n.Canonicalizer

// Initialize the security library
org.apache.xml.security.Init.init()

// Create some variables

// Get arguments

// Make sure required arguments have been provided

if(!error) {
    // Create some variables
    def ext = fileInName.tokenize('.').last()
    fileOutName = fileOutName ?: "${fileInName.lastIndexOf('.').with {it != -1 ? fileInName[0..<it] : fileInName}}_CANONICALIZED_AND_SORTED.${ext}"
    def fileIn = new File(fileInName)
    def fileOut = new File(fileOutName)
    def xsltFile = new File(xsltName)
    def temp1 = new File("./temp1")
    def temp2 = new File("./temp2")
    def os
    def is

    // Sort the XML attributes, remove comments, and remove extra whitespace
    println "Canonicalizing..."
    Canonicalizer c = Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N_OMIT_COMMENTS)
    os = temp1.newOutputStream()
    c.setWriter(os)
    c.canonicalize(fileIn.getBytes())
    os.close()

    // Sort the XML elements
    println "Sorting..."
    def factory = TransformerFactory.newInstance()
    is = xsltFile.newInputStream()
    def transformer = factory.newTransformer(new StreamSource(is))
    is.close()
    is = temp1.newInputStream()
    os = temp2.newOutputStream()
    transformer.transform(new StreamSource(is), new StreamResult(os))
    is.close()
    os.close()

    // Write the XML output in "pretty print"
    println "Beautifying..."
    def parser = new XmlParser()
    def printer = new XmlNodePrinter(new IndentPrinter(fileOut.newPrintWriter(), "    ", true))
    printer.print parser.parseText(temp2.getText())

    // Cleanup
    temp1.delete()
    temp2.delete()

    println "Done!"
}

完全なスクリプトはこちらです。

XSLT:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>
  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>
  <xsl:template match="foo">
    <foo>
      <xsl:apply-templates>
        <xsl:sort select="name()"/>
      </xsl:apply-templates>
    </foo>
  </xsl:template>
</xsl:stylesheet>

サンプル入力 XML:

<foo b="b" a="a" c="c">
    <qwer>
    <zxcv c="c" b="b"/>
    <vcxz c="c" b="b"/>
    </qwer>
    <baz e="e" d="d"/>
    <bar>
    <fdsa g="g" f="f"/>
    <asdf g="g" f="f"/>
    </bar>
</foo>

必要な出力 XML:

<foo a="a" b="b" c="c">
    <bar>
        <asdf f="f" g="g"/>
        <fdsa f="f" g="g"/>
    </bar>
    <baz d="d" e="e"/>
    <qwer>
        <vcxz b="b" c="c"/>
        <zxcv b="b" c="c"/>
    </qwer>
</foo>

要素のすべての子がアルファベット順になるように、すべての要素に変換を適用するにはどうすればよいですか?

4

2 に答える 2

8

変換をすべての要素に適用する場合は、特定の「foo」要素にのみ一致するテンプレートではなく、すべての要素に一致するテンプレートが必要です。

<xsl:template match="*">

要素を除外するには、「node()」に一致する現在のテンプレートを変更する必要があることに注意してください。

 <xsl:template match="node()[not(self::*)]|@*">

このテンプレート内では、属性を選択するためのコードも必要になります。これは、「foo」テンプレートが現時点ではそれらを無視するためです (<xsl:apply-templates />は属性を選択しません)。

実際、要件を見ると、項目 1 から 3 はすべて 1 つの XSLT で実行できます。たとえば、コメントを削除するには、現在 node() に一致するテンプレートからそれを無視することができます

<xsl:template match="node()[not(self::comment())][not(self::*)]|@*">

次の XSLT を試してみてください。ポイント 1 から 3 を達成する必要があります。

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

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

  <xsl:template match="*">
    <xsl:copy>
      <xsl:apply-templates select="@*">
        <xsl:sort select="name()"/>
      </xsl:apply-templates>
      <xsl:apply-templates>
        <xsl:sort select="name()"/>
      </xsl:apply-templates>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

編集: テンプレートは実際には、読みやすさを向上させる<xsl:template match="node()[not(self::comment())][not(self::*)]|@*">だけで置き換えることができます。<xsl:template match="processing-instruction()|@*">これは、"node()" が要素、テキスト ノード、コメント、および処理命令に一致するためです。XSLT では、要素は他のテンプレートによって取得され、テキスト ノードは組み込みテンプレートによって取得され、無視したいコメントは処理命令だけが残ります。

于 2013-09-09T22:15:39.047 に答える
3

楽しみのために、これをプログラムで行うこともできます。

def x = '''<foo b="b" a="a" c="c">
    <qwer>
    <!-- A comment -->
    <zxcv c="c" b="b"/>
    <vcxz c="c" b="b"/>
    </qwer>
    <baz e="e" d="d"/>
    <bar>
    <fdsa g="g" f="f"/>
    <asdf g="g" f="f"/>
    </bar>
</foo>'''

def order( node ) {
    [ *:node.attributes() ].sort().with { attr ->
        node.attributes().clear()
        attr.each { node.attributes() << it }
    }
    node.children().sort { it.name() }
                   .each { order( it ) }
    node
}

def doc = new XmlParser().parseText( x )

println groovy.xml.XmlUtil.serialize( order( doc ) )

ノードにコンテンツがある場合は、次のように変更する必要があります。

def x = '''<foo b="b" a="a" c="c">
    <qwer>
    <!-- A comment -->
    <zxcv c="c" b="b">Some Text</zxcv>
    <vcxz c="c" b="b"/>
    </qwer>
    <baz e="e" d="d">Woo</baz>
    <bar>
    <fdsa g="g" f="f"/>
    <asdf g="g" f="f"/>
    </bar>
</foo>'''

def order( node ) {
    [ *:node.attributes() ].sort().with { attr ->
        node.attributes().clear()
        attr.each { node.attributes() << it }
    }
    node.children().sort()
                   .grep( Node )
                   .each { order( it ) }
    node
}

def doc = new XmlParser().parseText( x )

println groovy.xml.XmlUtil.serialize( order( doc ) )

次に、次のようになります。

<?xml version="1.0" encoding="UTF-8"?><foo a="a" b="b" c="c">
  <baz d="d" e="e">Woo</baz>
  <bar>
    <fdsa f="f" g="g"/>
    <asdf f="f" g="g"/>
  </bar>
  <qwer>
    <zxcv b="b" c="c">Some Text</zxcv>
    <vcxz b="b" c="c"/>
  </qwer>
</foo>
于 2013-09-10T12:21:53.603 に答える