我想发现XML数据集的结构,我没有XML模式。作为此发现的一部分,我想计算数据集元素的最小和最大基数(minOccurs
和maxOccurs
)。我尝试过各种工具从XML文档生成XML模式,但它们不生成minOccurs
和maxOccurs
。但是,我怀疑使用XSLT(2.0 +)这样做是可行的。
更具体地说,假设我有以下XML文档:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<a/>
<b>
<c/>
</b>
<b/>
</root>
我希望能够以这样的形式计算基数:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<a minOccurs="1" maxOccurs="1"/>
<b minOccurs="2" maxOccurs="2">
<c minOccurs="0" maxOccurs="1"/>
</b>
</root>
root的子节点总是具有相同的最大和最小基数,因此可以使用以下内容计算该部分:
<xsl:template match="/*">
<xsl:element name="{name()}">
<xsl:for-each-group select="*" group-by="name()">
<xsl:sort select="current-grouping-key()"/>
<xsl:element name="{current-grouping-key()}">
<xsl:variable name="cardinality" select="count(current-group())"/>
<xsl:attribute name="minOccurs" select="$cardinality"/>
<xsl:attribute name="maxOccurs" select="$cardinality"/>
</xsl:element>
</xsl:for-each-group>
</xsl:element>
</xsl:template>
然而,我无法理解如何继续孙子的基数。我怀疑这可以被抽象为递归xsl:function
。
欢迎任何关于如何进行的建议!
答案 0 :(得分:3)
我不确定100%是否符合您的需求,但我想出了这个XSLT。它的工作原理是按路径名称对元素进行分组(例如&#34; root / a / b&#34;)
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0">
<xsl:output indent="yes"/>
<xsl:key name="parent_path" match="*" use="string-join(ancestor::*/name(), '/')" />
<xsl:key name="full_path" match="*" use="string-join(ancestor-or-self::*/name(), '/')" />
<xsl:template match="/*" priority="2">
<xsl:element name="{name()}">
<xsl:call-template name="element" />
</xsl:element>
</xsl:template>
<xsl:template match="*" name="element">
<xsl:variable name="path" select="string-join(ancestor-or-self::*/name(), '/')" />
<xsl:for-each-group select="key('parent_path', $path)" group-by="name()">
<xsl:sort select="current-grouping-key()"/>
<xsl:element name="{current-grouping-key()}">
<xsl:variable name="counts" select="key('full_path', $path)/count(*[name() = name(current())])" />
<xsl:variable name="min" select="min($counts)" />
<xsl:variable name="max" select="max($counts)"/>
<xsl:attribute name="minOccurs" select="if (not(contains($path, '/'))) then $max else $min"/>
<xsl:attribute name="maxOccurs" select="$max"/>
<xsl:apply-templates select="." />
</xsl:element>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
应用于此XML
<root>
<a/>
<b>
<c/>
<c/>
</b>
<b>
<c/>
<d>
<e />
</d>
<g></g>
<g></g>
<g></g>
</b>
<b>
<c/>
<d>
<e />
<e />
</d>
<g></g>
<g></g>
</b>
<a/>
</root>
以下是输出....
<root>
<a minOccurs="2" maxOccurs="2"/>
<b minOccurs="3" maxOccurs="3">
<c minOccurs="1" maxOccurs="2"/>
<d minOccurs="0" maxOccurs="1">
<e minOccurs="1" maxOccurs="2"/>
</d>
<g minOccurs="0" maxOccurs="3"/>
</b>
</root>