2010-12-13 109 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