在XSLT中组合来自2个列表的元素

时间:2010-12-13 09:27:31

标签: xml xslt

假设我的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。

4 个答案:

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

请注意

  1. 使用和覆盖标识规则是一种基本的XSLT设计模式,它的使用便于编写几乎任何转换。

  2. 使用<xsl:apply-templates/>代替<xsl:apply-templates select="node()[1]"/>允许并行执行,这可能会在不久的将来变得越来越重要。

  3. 密钥可用作优化,但对于小型XML文档(如提供的密钥)并不是必需的,并且密钥的使用与中心思想无关。解决这个问题。