XSLT生成元素基数

时间:2016-09-21 08:51:01

标签: xslt cardinality

我想发现XML数据集的结构,我没有XML模式。作为此发现的一部分,我想计算数据集元素的最小和最大基数(minOccursmaxOccurs)。我尝试过各种工具从XML文档生成XML模式,但它们不生成minOccursmaxOccurs。但是,我怀疑使用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

欢迎任何关于如何进行的建议!

1 个答案:

答案 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>