2010-12-13 102 views
1

可以说我有XML,看起来像这样(并假设我不能改变这个XML的格式):从2所列出元素相结合的XSLT

<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。

回答

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文档(如提供的)不是必需的,并且键的使用与解决此问题的中心思想无关。

1

你可以做的是使用XPath作为第二部分。在第一个列表中执行循环,只要有名称,就使用XPath在第二个列表中查询您想要的确切内容。

+0

是否需要在xslt中创建一个变量来表示循环当前所关注的值?或者我可以直接在Xpath查询中使用它? – jdoig 2010-12-13 10:35:49

+1

我认为这两个选项都可以有效。创建一个变量会给你更短的表达式,但我认为你可以在xpath查询中直接使用你的值。 – 2010-12-13 10:49:01

+0

太好了,谢谢。我不认为你有什么好的链接显示这样的技术吗?只是为了救我挣扎。干杯。 – jdoig 2010-12-13 10:52:55

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> 

设计是不理想,但这会奏效。我使用了密钥,假设输入树可能很大。

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的情况下更好的细粒度遍历。

+0

这里没有冒犯,但我真的很感兴趣,为什么要在身份验证的情况下使用身份验证,它使得更多的代码和imho的可读性更低? – Flack 2010-12-13 17:21:46

+1

@Flack:重用和语义含义:分组只是使用'apply-templates'而不是'copy-of'进行分组,然后您可以转换这些元素。另外,唯一的假设是:'bisquit'儿童被分组,组名以'name'开头。除此之外,模式可以在不改变样式表的情况下增长。 – 2010-12-13 17:39:45

+0

@Alejandro +1谢谢。我有你的观点。可能是因为90%的时间使用xml-html转换,这只是我的坏习惯。 – Flack 2010-12-13 17:47:23