我正在尝试对一些XML文档进行排序和规范化。期望的最终结果是:
除了#1,我已经实现了所有这些目标。
我一直使用this回答作为我的模板。以下是我到目前为止的情况:
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!"
}
完整脚本为here。
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>
如何将变换应用于所有元素,以便所有元素的子元素按字母顺序排列?
答案 0 :(得分:8)
如果要将变换应用于所有元素,则需要一个模板来匹配所有元素,而不是使模板与特定的“foo”元素匹配
<xsl:template match="*">
请注意,您必须更改与“node()”匹配的当前模板以排除元素:
<xsl:template match="node()[not(self::*)]|@*">
在此模板中,您还需要使用代码来选择属性,因为此时“foo”模板会忽略它们(<xsl:apply-templates />
不会选择属性)。
实际上,根据您的要求,第1至第3项都可以使用单个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中,元素由其他模板拾取,文本节点由内置模板拾取,您想要忽略的注释,只留下处理指令。
答案 1 :(得分: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>