Xslt 每个组的xsl,按选定的子元素和属性分组
我不熟悉XSL,但发现它非常有趣。我正在使用它来构建XSD,这样我们可以在与缺乏适当模式的非常旧的系统接口时使用数据绑定 我认为这是一个非常简单的问题,但经过几个小时的测试和谷歌搜索(开始怀疑我的谷歌技能…),我觉得我需要问:-) 鉴于此XML:Xslt 每个组的xsl,按选定的子元素和属性分组,xslt,xslt-2.0,xslt-grouping,Xslt,Xslt 2.0,Xslt Grouping,我不熟悉XSL,但发现它非常有趣。我正在使用它来构建XSD,这样我们可以在与缺乏适当模式的非常旧的系统接口时使用数据绑定 我认为这是一个非常简单的问题,但经过几个小时的测试和谷歌搜索(开始怀疑我的谷歌技能…),我觉得我需要问:-) 鉴于此XML: <?xml version="1.0" encoding="UTF-8"?> <classes> <class> <name>param1/stateSettings</na
<?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>
参数1/状态设置
0
参数2/状态设置
0
参数3/状态设置
1.
参数4/状态设置
0
我(最终)将获得以下输出:
<?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>
参数1/状态设置
参数2/状态设置
参数3/状态设置
参数4/状态设置
(分组键仅供参考,内容无关紧要)
到目前为止,我的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)是:
参数1/状态设置
参数2/状态设置
参数3/状态设置
参数4/状态设置
那么我的问题是什么?事实上,有两件事需要修正才能得到“最终”输出,但我只要解决一件就足够了
问题1与在中为每个组进行的分组有关。我需要根据选项的一些内容进行分组。我当前基于字符串的分组对选项元素的顺序很敏感。元素param1、param2和param3是相同的,应该在XSD中重构为一个simpleTypeparam4的键/值是反向的,应该创建为一个新的simpleType(是的,旧系统实际上使用这种“模式”…一点也不混乱:-D)
我不能将分组建立在整个选项的基础上,因为其中还有其他与此范围无关的内容,这将扰乱分组
问题2更具装饰性。如果第一个simpleType替换了最常见的类型就好了,也就是说,如果我有58个相同的类和2个稍有不同的类,我希望最常见的simpleType的名称不带序列号
(注意,default元素与在XSD中创建类型无关,但在创建XSD元素时,我将在稍后的XSL阶段重新讨论它们,XSD元素可以有默认值)
我希望有人能告诉我我错过了什么。请让我知道我的代码中可能存在的任何其他问题:-)据我所知,您首先要对
选项
元素进行排序,因此以下是您的代码加上一个用于排序的函数和一个插入的函数调用,以使用排序后的选项计算分组键:
<?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>
谢谢,效果很好!我在考虑一些分类,但我不知道如何完成。这并不是那么直观,这种语言:-)@dahook,XPath 3.1已经在XSLT 3.0和Saxon 9.7 PE和EE中得到了支持,它有一个
sort
函数来排序序列,所以我添加了一行来显示它的用法。但是,高阶函数和内联函数也是人们需要习惯的东西,如果它们在func中没有一些背景知识的话
<?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>
<xsl:for-each-group select="current-group()/list" group-by="string-join(sort(options/option, function($opt) { $opt/xs:integer(@key) }) ! (@key, @value), ':')">