xsl for-each-group,group-by selected子元素和属性

时间:2016-01-08 02:07:21

标签: xslt xslt-2.0 xslt-grouping

我是XSL的新手,但发现它非常有趣。我正在使用它来构建XSD,因此我们可以在连接一个缺乏正确模式的真正旧系统时使用数据绑定。

我坚持认为这是一个非常简单的问题,但经过几个小时的测试和谷歌搜索(开始怀疑我的Google技能......)我觉得我需要问: - )

鉴于此XML:

<?xml version="1.0" encoding="UTF-8"?>
<classes>
    <class>
        <name>param1/stateSettings</name>
        <list>
            <options>
                <default>0</default>
                <option key="0" value="Disabled"/>
                <option key="1" value="Enabled"/>
            </options>
        </list>
    </class>
    <class>
        <name>param2/stateSettings</name>
        <list>
            <options>
                <default>0</default>
                <option key="1" value="Enabled"/>
                <option key="0" value="Disabled"/>
            </options>
        </list>
    </class>
    <class>
        <name>param3/stateSettings</name>
        <list>
            <options>
                <default>1</default>
                <option key="1" value="Enabled"/>
                <option key="0" value="Disabled"/>
            </options>
        </list>
    </class>
    <class>
        <name>param4/stateSettings</name>
        <list>
            <options>
                <default>0</default>
                <option key="1" value="Disabled"/>
                <option key="0" value="Enabled"/>
            </options>
        </list>
    </class>
</classes>

我会(最终)获得此输出:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
   <xs:simpleType name="stateSettingsType">
      <xs:annotation>
         <xs:documentation>
            <replaces>param1/stateSettings</replaces>
            <replaces>param2/stateSettings</replaces>
            <replaces>param3/stateSettings</replaces>
            <grouping-key value="0:1:Disabled:Enabled"/>
         </xs:documentation>
      </xs:annotation>
      <xs:restriction base="xs:string">
         <xs:enumeration value="1">
            <xs:annotation>
               <xs:appinfo>
                  <dataBinding enum="Enabled"/>
               </xs:appinfo>
            </xs:annotation>
         </xs:enumeration>
         <xs:enumeration value="0">
            <xs:annotation>
               <xs:appinfo>
                  <dataBinding enum="Disabled"/>
               </xs:appinfo>
            </xs:annotation>
         </xs:enumeration>
      </xs:restriction>
   </xs:simpleType>
   <xs:simpleType name="stateSettings2Type">
      <xs:annotation>
         <xs:documentation>
            <replaces>param4/stateSettings</replaces>
            <grouping-key value="1:0:Disabled:Enabled"/>
         </xs:documentation>
      </xs:annotation>
      <xs:restriction base="xs:string">
         <xs:enumeration value="1">
            <xs:annotation>
               <xs:appinfo>
                  <dataBinding enum="Disabled"/>
               </xs:appinfo>
            </xs:annotation>
         </xs:enumeration>
         <xs:enumeration value="0">
            <xs:annotation>
               <xs:appinfo>
                  <dataBinding enum="Enabled"/>
               </xs:appinfo>
            </xs:annotation>
         </xs:enumeration>
      </xs:restriction>
   </xs:simpleType>
</xs:schema>

分组键仅供参考,内容无关紧要)

到目前为止,我的XSL是:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0">

    <xsl:output omit-xml-declaration="no" indent="yes"/>
    <xsl:output method="xml"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="options">
        <xsl:apply-templates select="option"/>
    </xsl:template>

    <xsl:template match="option">
        <xs:enumeration value="{@key}">
            <xs:annotation>
                <xs:appinfo>
                    <xsl:element name="dataBinding">
                        <xsl:attribute name="enum" select="@value"/>
                    </xsl:element>
                </xs:appinfo>
            </xs:annotation>
        </xs:enumeration>
    </xsl:template>

    <xsl:template match="name">
        <xsl:element name="replaces">
            <xsl:value-of select="../name"/>
        </xsl:element>
    </xsl:template>

    <xsl:template match="classes">
        <xsl:for-each-group select="class" group-by="substring-after(name, '/')">
            <xsl:variable name="typeName" select="current-grouping-key()"/>
            <xsl:for-each-group select="current-group()/list" group-by="string-join((options/option/@key, options/option/@value), ':')">
                <xsl:variable name="newTypeName">
                <xsl:choose>
                    <xsl:when test="position() gt 1">
                        <xsl:value-of select="string-join(($typeName,format-number(position(),'#'),'Type'),'')"/>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:value-of select="string-join(($typeName,'Type'),'')"/>
                    </xsl:otherwise>
                </xsl:choose>
                </xsl:variable>
                <xs:simpleType name="{$newTypeName}">
                    <xs:annotation>
                        <xs:documentation>
                            <xsl:for-each select="current-group()">
                                <xsl:apply-templates select="../name"/>
                            </xsl:for-each>
                            <xsl:element name="grouping-key">
                                <xsl:attribute name="value" select="current-grouping-key()"/>
                            </xsl:element>
                        </xs:documentation>
                    </xs:annotation>
                    <xs:restriction base="xs:string">
                        <xsl:apply-templates select="options"/>
                    </xs:restriction>
                </xs:simpleType>
            </xsl:for-each-group>
        </xsl:for-each-group>
    </xsl:template>

    <xsl:template match="/">
        <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
            <xsl:apply-templates select="classes"/>
        </xs:schema>
    </xsl:template>

</xsl:stylesheet>

我当前的XSL输出(XSD)是:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
   <xs:simpleType name="stateSettingsType">
      <xs:annotation>
         <xs:documentation>
            <replaces>param1/stateSettings</replaces>
            <grouping-key value="0:1:Disabled:Enabled"/>
         </xs:documentation>
      </xs:annotation>
      <xs:restriction base="xs:string">
         <xs:enumeration value="0">
            <xs:annotation>
               <xs:appinfo>
                  <dataBinding enum="Disabled"/>
               </xs:appinfo>
            </xs:annotation>
         </xs:enumeration>
         <xs:enumeration value="1">
            <xs:annotation>
               <xs:appinfo>
                  <dataBinding enum="Enabled"/>
               </xs:appinfo>
            </xs:annotation>
         </xs:enumeration>
      </xs:restriction>
   </xs:simpleType>
   <xs:simpleType name="stateSettings2Type">
      <xs:annotation>
         <xs:documentation>
            <replaces>param2/stateSettings</replaces>
            <replaces>param3/stateSettings</replaces>
            <grouping-key value="1:0:Enabled:Disabled"/>
         </xs:documentation>
      </xs:annotation>
      <xs:restriction base="xs:string">
         <xs:enumeration value="1">
            <xs:annotation>
               <xs:appinfo>
                  <dataBinding enum="Enabled"/>
               </xs:appinfo>
            </xs:annotation>
         </xs:enumeration>
         <xs:enumeration value="0">
            <xs:annotation>
               <xs:appinfo>
                  <dataBinding enum="Disabled"/>
               </xs:appinfo>
            </xs:annotation>
         </xs:enumeration>
      </xs:restriction>
   </xs:simpleType>
   <xs:simpleType name="stateSettings3Type">
      <xs:annotation>
         <xs:documentation>
            <replaces>param4/stateSettings</replaces>
            <grouping-key value="1:0:Disabled:Enabled"/>
         </xs:documentation>
      </xs:annotation>
      <xs:restriction base="xs:string">
         <xs:enumeration value="1">
            <xs:annotation>
               <xs:appinfo>
                  <dataBinding enum="Disabled"/>
               </xs:appinfo>
            </xs:annotation>
         </xs:enumeration>
         <xs:enumeration value="0">
            <xs:annotation>
               <xs:appinfo>
                  <dataBinding enum="Enabled"/>
               </xs:appinfo>
            </xs:annotation>
         </xs:enumeration>
      </xs:restriction>
   </xs:simpleType>
</xs:schema>

那么我的问题是什么?嗯,实际上有两件事需要修复以获得&#34;终极&#34;输出,但我只需解决一个就足够了。

问题1与for-each-group中完成的分组有关。我需要根据选项部分进行分组。我当前基于字符串的分组对选项元素的顺序很敏感。元素 param1 param2 param3 是相同的,应该重构为XSD中的一个simpleType。 param4 具有反转的键/值,应该创建为一个新的simpleType(是的,旧系统实际上使用了这种&#34;架构&#34; ...根本不会混淆: -D) 我不能将分组基于整个选项,因为其中有其他内容与此范围无关,并且会使分组陷入混乱。

问题2更具美感。如果第一个simpleType替换最常见的类型会很好,即如果我有58个相同的且2有些不同我希望最常见的simpleType具有没有序列的名称号。

(注意默认元素与在XSD中创建类型无关,但在创建xsd元素时,我将在我的XSL的后期重新访问它们,这些元素可以具有默认值)< / p>

我希望有人能够告诉我我失踪的东西。请告诉我代码中可能遇到的任何其他问题: - )

1 个答案:

答案 0 :(得分:2)

据我了解,您首先要对option元素进行排序,因此以下是您的代码加上一个用于排序的函数和一个插入函数调用来计算具有排序选项的分组键: / p>

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0"
    xmlns:mf="http://example.com/mf"
    exclude-result-prefixes="mf">

    <xsl:output omit-xml-declaration="no" indent="yes"/>
    <xsl:output method="xml"/>
    <xsl:strip-space elements="*"/>

    <xsl:function name="mf:sort" as="element(option)*">
        <xsl:param name="input" as="element(option)*"/>
        <xsl:perform-sort select="$input">
            <xsl:sort select="xs:integer(@key)"/>
        </xsl:perform-sort>
    </xsl:function>

    <xsl:template match="options">
        <xsl:apply-templates select="option"/>
    </xsl:template>

    <xsl:template match="option">
        <xs:enumeration value="{@key}">
            <xs:annotation>
                <xs:appinfo>
                    <xsl:element name="dataBinding">
                        <xsl:attribute name="enum" select="@value"/>
                    </xsl:element>
                </xs:appinfo>
            </xs:annotation>
        </xs:enumeration>
    </xsl:template>

    <xsl:template match="name">
        <xsl:element name="replaces">
            <xsl:value-of select="../name"/>
        </xsl:element>
    </xsl:template>

    <xsl:template match="classes">
        <xsl:for-each-group select="class" group-by="substring-after(name, '/')">
            <xsl:variable name="typeName" select="current-grouping-key()"/>
            <xsl:for-each-group select="current-group()/list" group-by="string-join(for $opt in mf:sort(options/option) return ($opt/@key, $opt/@value), ':')">
                <xsl:variable name="newTypeName">
                <xsl:choose>
                    <xsl:when test="position() gt 1">
                        <xsl:value-of select="string-join(($typeName,format-number(position(),'#'),'Type'),'')"/>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:value-of select="string-join(($typeName,'Type'),'')"/>
                    </xsl:otherwise>
                </xsl:choose>
                </xsl:variable>
                <xs:simpleType name="{$newTypeName}">
                    <xs:annotation>
                        <xs:documentation>
                            <xsl:for-each select="current-group()">
                                <xsl:apply-templates select="../name"/>
                            </xsl:for-each>
                            <xsl:element name="grouping-key">
                                <xsl:attribute name="value" select="current-grouping-key()"/>
                            </xsl:element>
                        </xs:documentation>
                    </xs:annotation>
                    <xs:restriction base="xs:string">
                        <xsl:apply-templates select="options"/>
                    </xs:restriction>
                </xs:simpleType>
            </xsl:for-each-group>
        </xsl:for-each-group>
    </xsl:template>

    <xsl:template match="/">
        <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
            <xsl:apply-templates select="classes"/>
        </xs:schema>
    </xsl:template>

</xsl:stylesheet>

在带有XPath 3.1的XSLT 3.0中(已经由Saxon 9.7 PE和EE支持),您可以使用XPath sort函数和!运算符来获得更紧凑的表达式:

<xsl:for-each-group select="current-group()/list" group-by="string-join(sort(options/option, function($opt) { $opt/xs:integer(@key) }) ! (@key, @value), ':')">