这是源XML:
<customers>
<firstname1>Sean</firstname1>
<lastname1>Killer</lastname1>
<sex1>M</sex1>
<firstname2>Frank</firstname2>
<lastname2>Woods</lastname2>
<sex2>M</sex2>
<firstname3>Jennifer</firstname3>
<lastname3>Lee</lastname3>
<sex3>F</sex3>
</customers>
如何将其转换为此?
<MyCustomers>
<Customer>
<Name> Sean Killer</Name>
<Sex>M</Sex>
</Customer>
<Customer>
<Name> Frank Woods</Name>
<Sex>M</Sex>
</Customer>
<Customer>
<Name>Jennifer Lee</Name>
<Sex>F</Sex>
</Customer>
</MyCustomers>
答案 0 :(得分:4)
根据评论:
如果元素不在后续订单中会怎样?
在这种情况下(假设XSLT 1.0),您可以使用translate()
获取元素的id,然后使用concat()
构建的正确名称搜索相应的元素。我会将following-sibling::
轴更改为../
(parent::
的缩写),以确保最终捕获当前firstname
之前的元素。
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="customers">
<MyCustomers>
<xsl:apply-templates select="*[starts-with(name(),'firstname')]"/>
</MyCustomers>
</xsl:template>
<xsl:template match="*[starts-with(name(),'firstname')]">
<xsl:variable name="id" select="translate(name(),'firstname','')"/>
<Customer>
<Name><xsl:value-of select="concat(.,' ',
../*[name()=concat('lastname',$id)])"/></Name>
<Sex><xsl:value-of select="../*[name()=concat('sex',$id)]"/></Sex>
</Customer>
</xsl:template>
</xsl:stylesheet>
过时的回答
假设问题中显示的是固定输入文档结构,那么精细工作的XSLT 1.0转换是:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="customers">
<MyCustomers>
<xsl:apply-templates select="*[starts-with(name(),'firstname')]"/>
</MyCustomers>
</xsl:template>
<xsl:template match="*[starts-with(name(),'firstname')]">
<Customer>
<Name><xsl:value-of select="concat(.,' ',
following-sibling::*[1]
[starts-with(name(),'lastname')])"/></Name>
<Sex><xsl:value-of select="following-sibling::*[2]
[starts-with(name(),'sex')]"/></Sex>
</Customer>
</xsl:template>
</xsl:stylesheet>
小解释
您需要XPath 1.0函数starts-with()
,因为XML输入中标记的名称令人遗憾。您可以使用following-sibling::
轴获取名称以firstname
开头的任何元素所需的以下兄弟标记。
答案 1 :(得分:0)
这是一个XSLT 2.0样式表,它将获得您正在寻找的输出,即使它们不是有序的。它还按“firstname”元素名称排序。
示例XML输入(混合显示不同的顺序):
<customers>
<lastname1>Killer</lastname1>
<sex3>F</sex3>
<firstname2>Frank</firstname2>
<firstname1>Sean</firstname1>
<lastname2>Woods</lastname2>
<sex2>M</sex2>
<firstname3>Jennifer</firstname3>
<sex1>M</sex1>
<lastname3>Lee</lastname3>
</customers>
XSLT 2.0样式表(使用Saxon-HE 9.3测试):
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:choose>
<xsl:when test="name()[starts-with(.,'firstname')]">
<xsl:variable name="suffix" select="substring(name(),10)"></xsl:variable>
<xsl:message><xsl:value-of select="$suffix"/></xsl:message>
<customer>
<Name>
<xsl:value-of select="concat(.,' ',/customers/*[starts-with(name(),'lastname')][ends-with(name(),$suffix)])"/>
</Name>
<Sex>
<xsl:value-of select="/customers/*[starts-with(name(),'sex')][ends-with(name(),$suffix)]"/>
</Sex>
</customer>
</xsl:when>
<xsl:when test="name()='customers'">
<MyCustomers>
<xsl:apply-templates>
<xsl:sort select="name()[starts-with(.,'firstname')]"></xsl:sort>
</xsl:apply-templates>
</MyCustomers>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="node()|@*"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
<强>输出:强>
<MyCustomers>
<customer>
<Name>Sean Killer</Name>
<Sex>M</Sex>
</customer>
<customer>
<Name>Frank Woods</Name>
<Sex>M</Sex>
</customer>
<customer>
<Name>Jennifer Lee</Name>
<Sex>F</Sex>
</customer>
</MyCustomers>
答案 2 :(得分:0)
即使top元素的子元素以任意方式洗牌,此转换也会产生想要的结果:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="vNumCustomers"
select="count(/*/*) div 3"/>
<xsl:template match="/*">
<MyCustomers>
<xsl:for-each select=
"*[not(position() > $vNumCustomers)]">
<xsl:variable name="vNum" select="position()"/>
<Customer>
<Name>
<xsl:value-of select=
"concat(/*/*[name()=concat('firstname',$vNum)],
' ',
/*/*[name()=concat('lastname',$vNum)]
)
"/>
</Name>
<Sex>
<xsl:value-of select=
"/*/*[name()=concat('sex',$vNum)]
"/>
</Sex>
</Customer>
</xsl:for-each>
</MyCustomers>
</xsl:template>
</xsl:stylesheet>
应用于此XML文档(对提供的文档进行任意重新洗牌):
<customers>
<sex1>M</sex1>
<lastname2>Woods</lastname2>
<lastname1>Killer</lastname1>
<sex2>M</sex2>
<firstname3>Jennifer</firstname3>
<firstname2>Frank</firstname2>
<lastname3>Lee</lastname3>
<firstname1>Sean</firstname1>
<sex3>F</sex3>
</customers>
产生了想要的正确结果:
<MyCustomers>
<Customer>
<Name>Sean Killer</Name>
<Sex>M</Sex>
</Customer>
<Customer>
<Name>Frank Woods</Name>
<Sex>M</Sex>
</Customer>
<Customer>
<Name>Jennifer Lee</Name>
<Sex>F</Sex>
</Customer>
</MyCustomers>
<强>解释强>:
我们计算出现数据的客户数量。变量$vNumCustomers
保存此数据。
对于每个客户{i}(i = 1到$vNumCustomers
),我们会创建相应的<Customer{i}>
元素。为了避免使用递归,我们在这里使用 the Piez method 。