2014-03-04 50 views
0

我有一個樣式表需要兩個文件,一個來自工程回購庫,另一個來自文檔回購庫,併合並它們以創建一些DITA文件(進一步處理)。最近,我試圖將文檔文件的內容分割成通用文件和特定文件。因此,我的合併現在是一個具有兩個文檔文件的工程文件。XSLT中的雙斜槓

通用文件,因爲它是:

<?xml version="1.0" encoding="UTF-8"?> 
<messages xmlns:xs="http://www.w3.org/2001/XMLSchema"> 
    <message id="IDENT_STRING"> 
    .... 
    </message> 
</messages> 

的具體文件有一個實體標籤的通用文件指出:

<?xml version="1.0" encoding="UTF-8" standalone="no"?> 
<!DOCTYPE messages [ 
<!ENTITY generic-file SYSTEM "generic-file.xml"> 
]> 
<messages> &generic-file; <!-- specific-file --> 
    <message id="IDENT_STRING2"> 
    .... 
    </message> 
</messages> 

的選擇是這樣寫的:

<xsl:copy-of select="$docid/message[@id=$id]/doc/explanation/text()"/> 

這隻會抓取特定文件中的內容。直到我改變選擇有兩個斜槓,我的樣式表正常工作。這是正確的版本:

<xsl:copy-of select="$docid//message[@id=$id]/doc/explanation/text()"/> 

我對社區的問題是1)爲什麼第二個語法正確? 2)我如何更迅速地發現它?

回答

2

下面是關於XPath該/ X/Y // z是什麼樣的信息的良好來源,http://www.w3.org/TR/xpath/#location-paths

從縮寫語法部分: //短的/後裔或自身::節點()/。例如,// para是/ descendant-or-self :: node()/ child :: para的縮寫,因此將選擇文檔中的任何para元素(即使是作爲文檔元素的para元素也將被//para,因爲文檔元素節點是根節點的子節點); div // para是div/descendant-or-self :: node()/ child :: para的縮寫,因此將選擇div子元素的所有para子元素。

//是否在xpath的開始或中間,它的含義是相同的。

至於學習這個東西,對我來說,我不得不構建一個小的人工xml或簡化的xml shell,我試圖轉換並運行xslt。在Visual Studio中,2005年左右有一個相當方便的方法可以做到這一點,但我認爲它仍然存在,儘管我沒有在一段時間內搞混它。我發現MSDN或w3.org是很好的資源,儘管w3上的語言有時可能會消化。

使用此XML:

<?xml version="1.0" encoding="utf-8"?> 
<root> 
    <a id="a1"> 
     <d id="1" /> 
     <d id="2" /> 
    </a> 
    <a id="a2"> 
     <d id="3" /> 
     <d id="4" /> 
    </a> 
    <b> 
     <c id="1" /> 
     <c id="2" /> 
    </b> 
</root> 

有了這個XSLT:

<?xml version="1.0" encoding="utf-8"?> 
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 

    <xsl:output method="xml" indent="yes"/> 
    <xsl:strip-space elements="*"/> 

    <xsl:template match="/"> 
     <test> 
      <xsl:apply-templates/> 
     </test> 
    </xsl:template> 

    <xsl:template match="b"> 
     <z> 
      <select>//a</select> 
      <xsl:copy-of select="//a"/> 
     </z> 
     <z> 
      <select>.//a</select> 
      <xsl:copy-of select=".//a"/> 
     </z> 
     <z> 
      <select>.//c</select> 
      <xsl:copy-of select=".//c"/> 
     </z> 
    <z> 
     <select>/root/a/d</select> 
     <xsl:copy-of select="/root/a/d"/> 
    </z> 
    <z> 
      <select>/root/a</select> 
      <xsl:copy-of select="/root/a"/> 
     </z> 
     <z> 
      <question>so what is the node?</question> 
      <period> 
       <xsl:copy-of select="."/> 
      </period> 
      <slash> 
       <xsl:copy-of select="/"/> 
      </slash> 
     </z> 
    </xsl:template> 
</xsl:stylesheet> 

得到這樣的結果:構建一個XPath時

<?xml version="1.0" encoding="utf-8"?> 
<test> 
    <z> 
    <select>//a</select> 
    <a id="a1"> 
     <d id="1" /> 
     <d id="2" /> 
    </a> 
    <a id="a2"> 
     <d id="3" /> 
     <d id="4" /> 
    </a> 
    </z> 
    <z> 
    <select>.//a</select> 
    </z> 
    <z> 
    <select>.//c</select> 
    <c id="1" /> 
    <c id="2" /> 
    </z> 
    <z> 
    <select>/root/a/d</select> 
    <d id="1" /> 
    <d id="2" /> 
    <d id="3" /> 
    <d id="4" /> 
    </z> 
    <z> 
    <select>/root/a</select> 
    <a id="a1"> 
     <d id="1" /> 
     <d id="2" /> 
    </a> 
    <a id="a2"> 
     <d id="3" /> 
     <d id="4" /> 
    </a> 
    </z> 
    <z> 
    <question>so what is the node?</question> 
    <period> 
     <b> 
     <c id="1" /> 
     <c id="2" /> 
     </b> 
    </period> 
    <slash> 
     <root> 
     <a id="a1"> 
      <d id="1" /> 
      <d id="2" /> 
     </a> 
     <a id="a2"> 
      <d id="3" /> 
      <d id="4" /> 
     </a> 
     <b> 
      <c id="1" /> 
      <c id="2" /> 
     </b> 
     </root> 
    </slash> 
    </z> 
</test> 

所以,//將獲得的後代那個節點類型,如果你從一個xpath開始//你將要去看文檔。

如果你開始。那麼你正在用當前節點啓動你的xpath(如果你使用xsl:apply-templates,當前節點就是在xsl:template匹配後的任何事情,但是如果你使用xsl:call-template,那麼當前節點是相同的作爲您創建xsl:call-template的當前節點)。

如果你用/來啓動xpath,那麼你引用了你的文檔的根目錄。

推測$ docId是一個節點集,它指向一個文檔或另一個文檔,它設置了xpath的起始點,所以$ docId // message表示獲取節點集中所有$ docId的消息元素。

最後你需要//的原因是你沒有完全指定路徑。在我提供的示例中,我構建了一個/ root/a/d,它是d元素的完整路徑,並將其全部4個拉出。 $ docId //消息只是允許您查找$ docId節點集根目錄下的任何元素'消息'。有時你可能沒有一個乾淨的對稱來獲取記錄:例如,如果你有/ messages/critical/message和/ messages/warning/message,使用xpath of/messages //消息或者可能會更方便//消息來獲得你需要的東西。

我希望這會有所幫助。

+0

這些都是很好的答案 - 很難選擇哪一個纔是答案。非常感謝。 –

1

沒有看到$docid變量的內容,您的問題很難診斷。但我會試一試。

我認爲這個變量包含類似:

<xsl:variable name="docid" select="document('generic-file.xml')"/> 

現在,//element檢索元素(在這種情況下,這些名稱爲「元素」),不論是文檔樹在那裏 - ,更重要的是,不管你在哪裏都在樹上。換句話說,以//開頭的表達式不依賴於上下文。

因此,下面

<xsl:copy-of select="$docid//message[@id=$id]/doc/explanation/text()"/> 

指:從外部文檔中檢索無論他們在哪裏,如果他們的id屬性對應於某一變$id所有message元素。然後,查找子元素doc及其子元素explanation並複製後者的文本節點。


在另一方面,本

<xsl:copy-of select="$docid/message[@id=$id]/doc/explanation/text()"/> 

指:從外部文件檢索是根元件和被稱爲「信息」的元素。但顯然,在你的XML中沒有根元素message。根元素有messages。因此,這本來是可行的:

<xsl:copy-of select="$docid/messages/message[@id=$id]/doc/explanation/text()"/> 

/的表達依賴於其中被執行的上下文。


更詳細。下面的轉換說明雖然模板匹配c元素(所以上下文是c),但a元素可以用//檢索。

輸入

<?xml version="1.0" encoding="UTF-8"?> 
<root> 
    <a/> 
    <b> 
     <c/> 
    </b> 
</root> 

樣式表

<?xml version="1.0" encoding="utf-8"?> 

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 

    <xsl:output method="xml" indent="yes"/> 
    <xsl:strip-space elements="*"/> 

    <xsl:template match="/"> 
     <xsl:apply-templates/> 
    </xsl:template> 

    <xsl:template match="c"> 
     <xsl:copy-of select="//a"/> 
    </xsl:template> 

</xsl:stylesheet> 

輸出

<?xml version="1.0" encoding="UTF-8"?> 
<a/> 

如果模板被改變到t他如下:

<xsl:template match="c"> 
     <xsl:copy-of select=".//a"/> 
</xsl:template> 

沒有輸出。

+2

//從任何你所在的地方得到所有後代......而不是從文檔中的任何地方。如果你做/ x/y [@ id = $ id] // z你只會得到那個特定y下的z而不是每個z都到處 – Felan

+0

@Felan'select =「// z」'的行爲(選擇* * all ** z)不同於'select =「y // z」'(在y下選擇z)。第二種情況就是你所描述的,而第一種情況就是我所說的,我假設它出現在OP的代碼中。你錯誤地使用了''後代::'軸的''''。 –

+0

// z從任何點都選擇所有z。行爲上,無論是在頂部還是在中部,它都是相同的。所有關於上下文的討論都令人困惑,在我看來,這是令人誤解的。如果你的xml的結構是z/y/x/v/u,你的xpath是/ z/y,那麼你將得到所有你的x和v,如果你的xpath是// u,那麼你無論z,y's,x's和v是什麼,都可以獲得所有的u。 – Felan