假设我的XML看起来像这样(并假设我无法改变这种XML的格式):
<biscuit>
<name>Hobnobs</name>
<price>1.49</price>
<name>Digestives</name>
<price>89.00</price>
</biscuit>
<biscuitInfo name="Hobnobs">
<nutritionalValue>
<fat>6 grams</fat>
<sugar>lots</sugar>
</nutritionalValue>
</biscuitInfo>
<biscuitInfo name="Digestives">
<nutritionalValue>
<fat>3 grams</fat>
<sugar>5 grams</sugar>
</nutritionalValue>
</biscuitInfo>
我想使用XSLT将其变成看起来像这样的东西:
<biscuit>
<name>Hobnobs</name>
<price>1.49</price>
<fat>6 grams</fat>
<sugar>lots</sugar>
</biscuit>
我如何在XSLT中做这样的事情?我可以遍历第一个饼干列表(名称和价格)并从第二个列表中提取元素(营养价值)吗?
我对XSL不太了解,所以欢迎任何建议。
干杯,
JD。
答案 0 :(得分:2)
两个例子。这个样式表使用了clasic完全递归:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="kNutChildByBisName" match="nutritionalValue/*"
use="../../@name"/>
<xsl:key name="kElemByPrecedingName" match="biscuit/*[not(self::name)]"
use="preceding-sibling::name[1]"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="name" mode="group">
<bisquit>
<xsl:apply-templates select=".|key('kNutChildByBisName',.)|
key('kElemByPrecedingName',.)"/>
</bisquit>
</xsl:template>
<xsl:template match="biscuit">
<xsl:apply-templates mode="group"/>
</xsl:template>
<xsl:template match="biscuitInfo"/>
<xsl:template match="node()" mode="group"/>
</xsl:stylesheet>
这个样式表使用细粒度遍历:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:key name="kNutByBisName" match="nutritionalValue"
use="../@name"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()[1]|@*"/>
</xsl:copy>
<xsl:apply-templates select="following-sibling::node()[1]"/>
</xsl:template>
<xsl:template match="biscuitInfo"/>
<xsl:template match="biscuit">
<xsl:apply-templates select="node()[1]|following-sibling::node()[1]"/>
</xsl:template>
<xsl:template match="name[1]" name="group">
<bisquit>
<xsl:call-template name="identity"/>
<xsl:apply-templates select="key('kNutByBisName',.)/node()[1]"/>
</bisquit>
<xsl:apply-templates select="following-sibling::name[1]" mode="group"/>
</xsl:template>
<xsl:template match="name"/>
<xsl:template match="name" mode="group">
<xsl:call-template name="group"/>
</xsl:template>
</xsl:stylesheet>
使用此输入:
<root>
<biscuit>
<name>Hobnobs</name>
<price>1.49</price>
<name>Digestives</name>
<price>89.00</price>
</biscuit>
<biscuitInfo name="Hobnobs">
<nutritionalValue>
<fat>6 grams</fat>
<sugar>lots</sugar>
</nutritionalValue>
</biscuitInfo>
<biscuitInfo name="Digestives">
<nutritionalValue>
<fat>3 grams</fat>
<sugar>5 grams</sugar>
</nutritionalValue>
</biscuitInfo>
</root>
两个输出:
<root>
<bisquit>
<name>Hobnobs</name>
<price>1.49</price>
<fat>6 grams</fat>
<sugar>lots</sugar>
</bisquit>
<bisquit>
<name>Digestives</name>
<price>89.00</price>
<fat>3 grams</fat>
<sugar>5 grams</sugar>
</bisquit>
</root>
注意:您正在执行两项任务:分组和交叉引用。
修改:如果组中只有name
,则会进行更精细的细粒度遍历。
答案 1 :(得分:1)
你可以做的是在第二部分使用XPath。在第一个列表上执行循环,并在获得名称后立即使用XPath在第二个列表中查询所需的内容。
答案 2 :(得分:1)
这个XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="bisquit-info" match="/root/biscuitInfo" use="@name"/>
<xsl:template match="root">
<xsl:copy>
<xsl:apply-templates select="biscuit/name"/>
</xsl:copy>
</xsl:template>
<xsl:template match="name">
<bisquit>
<xsl:copy-of select="."/>
<xsl:copy-of select="following-sibling::price[1]"/>
<xsl:copy-of select="key('bisquit-info', .)/nutritionalValue/*"/>
</bisquit>
</xsl:template>
</xsl:stylesheet>
使用此类XML输入(仅为良好性添加根节点)
<root>
<biscuit>
<name>Hobnobs</name>
<price>1.49</price>
<name>Digestives</name>
<price>89.00</price>
</biscuit>
<biscuitInfo name="Hobnobs">
<nutritionalValue>
<fat>6 grams</fat>
<sugar>lots</sugar>
</nutritionalValue>
</biscuitInfo>
<biscuitInfo name="Digestives">
<nutritionalValue>
<fat>3 grams</fat>
<sugar>5 grams</sugar>
</nutritionalValue>
</biscuitInfo>
</root>
将提供此结果:
<root>
<bisquit>
<name>Hobnobs</name>
<price>1.49</price>
<fat>6 grams</fat>
<sugar>lots</sugar>
</bisquit>
<bisquit>
<name>Digestives</name>
<price>89.00</price>
<fat>3 grams</fat>
<sugar>5 grams</sugar>
</bisquit>
</root>
设计并不理想,但这可行。我使用了键,假设输入树可能很大。
答案 3 :(得分:1)
传统的身份规则会被一些模板覆盖。没有钥匙,没有精细的遍历:
<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="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="biscuit/name">
<biscuit>
<xsl:call-template name="identity"/>
<xsl:apply-templates select=
"following-sibling::price[1]
|
../../biscuitInfo[@name = current()]/*"/>
</biscuit>
</xsl:template>
<xsl:template match="biscuit">
<xsl:apply-templates select="name"/>
</xsl:template>
<xsl:template match="nutritionalValue">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="biscuitInfo"/>
</xsl:stylesheet>
应用于此源XML (基本上是提供的,但包含在单个顶部元素中):
<product>
<biscuit>
<name>Hobnobs</name>
<price>1.49</price>
<name>Digestives</name>
<price>89.00</price>
</biscuit>
<biscuitInfo name="Hobnobs">
<nutritionalValue>
<fat>6 grams</fat>
<sugar>lots</sugar>
</nutritionalValue>
</biscuitInfo>
<biscuitInfo name="Digestives">
<nutritionalValue>
<fat>3 grams</fat>
<sugar>5 grams</sugar>
</nutritionalValue>
</biscuitInfo>
</product>
产生了想要的正确结果:
<product>
<biscuit>
<name>Hobnobs</name>
<price>1.49</price>
<fat>6 grams</fat>
<sugar>lots</sugar>
</biscuit>
<biscuit>
<name>Digestives</name>
<price>89.00</price>
<fat>3 grams</fat>
<sugar>5 grams</sugar>
</biscuit>
</product>
请注意:
使用和覆盖标识规则是一种基本的XSLT设计模式,它的使用便于编写几乎任何转换。
使用<xsl:apply-templates/>
代替<xsl:apply-templates select="node()[1]"/>
允许并行执行,这可能会在不久的将来变得越来越重要。
密钥可用作优化,但对于小型XML文档(如提供的密钥)并不是必需的,并且密钥的使用与中心思想无关。解决这个问题。