2013-11-27 51 views
1

我在使用XSLT檢查節點是否存在以及是否將它們添加到文檔中時遇到了一些問題。這裏是我的情況:XSLT添加新節點(如果它們不存在)

輸入

<Message> 
    <a>123</a> 
    <c>456</c> 
    <d>789</d> 
</Message> 

所需的輸出

<MsgHead> 
    <Document> 
    <Message> 
    <a>123</a> 
    <b>-1</b> 
    <c>456</c> 
    <d>789</d> 
    </Message> 
    <Document> 
</MsgHead> 

我也有 「默認值」

默認給出下面的靜態文件

<DefaultNodes> 
    <a>-1</a> 
    <b>-1</b> 
    <c>-1</c> 
    <d>-1</d> 
</DefaultNodes> 

輸入文件有不同數量的節點,我需要使用缺失的缺省節點「完成」它們。節點名稱顯然不是a,b,c等,但有700個不同的默認值的不同節點。

這裏是我的嘗試,到目前爲止 我的XSLT

<xsl:stylesheet version="1.0" 
       xmlns:xsl="http://www.w3.org/1999/XSL/Transform">  
    <xsl:template match="/"> 
     <MsgHead> 
      <Document> 
       <Message> 
        <xsl:apply-templates></xsl:apply-templates> 
       </Message> 
      </Document> 
     </MsgHead> 
    </xsl:template> 

    <xsl:template match="Message"> 
     <xsl:copy-of select="node()"/> 
     <xsl:for-each select="document('default-nodes.xml')/DefaultNodes/*"> 
      <xsl:choose> 
       <xsl:when test="//*[local-name(current())]"> <!-- This is the line giving me trouble --> 
        <!--Node already present, do nothing-->      
       </xsl:when> 
       <xsl:otherwise> 
        <!--Node not in input, add from the defaults file --> 
        <xsl:copy-of select="self::node()"/> 
       </xsl:otherwise> 
      </xsl:choose> 
     </xsl:for-each> 
    </xsl:template> 
</xsl:stylesheet> 

這幾乎是工作,但它似乎無法找到,如果存在與否的節點。我正在使用的當前測試(// * [local-name(current())])似乎無論如何都會返回true。任何人有任何建議我如何解決這個問題?

謝謝!

回答

3

我使用(// * [本地名稱(當前()))似乎返回當前的測試true無論如何

是的,因爲(在您測試它的上下文中)該測試意味着「如果此模板的當前節點的本地名稱非空,請選擇整個default-nodes.xml文檔中的所有元素,否則不要選擇所有」。

我假設default-nodes.xml定義了您希望結果元素顯示的順序,並且DefaultNodes下的所有元素都有不同的名稱。在這種情況下,怎麼樣:

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

    <!-- store a reference to the root of the main input tree --> 
    <xsl:variable name="root" select="/" /> 

    <xsl:template match="/"> 
     <MsgHead> 
      <Document> 
       <Message> 
        <xsl:apply-templates select="document('default-nodes.xml')/DefaultNodes/*"/> 
       </Message> 
      </Document> 
     </MsgHead> 
    </xsl:template> 

    <xsl:template match="DefaultNodes/*"> 
     <!-- look for a matching element in the main input tree --> 
     <xsl:variable name="sourceNode" select="$root/Message/*[name() = name(current())]" /> 
     <xsl:choose> 
      <!-- if such a node exists, copy it --> 
      <xsl:when test="$sourceNode"> 
       <xsl:copy-of select="$sourceNode" /> 
      </xsl:when> 
      <!-- else copy the default one --> 
      <xsl:otherwise> 
       <xsl:copy-of select="." /> 
      </xsl:otherwise> 
     </xsl:choose> 
    </xsl:template> 
</xsl:stylesheet> 

在這裏,我應用模板到默認元素,而不是輸入的。

請注意,使用變量$root來保存對主輸入樹的引用。在DefaultNodes/*模板中,當前節點來自default-nodes.xml,因此在該上下文中/表示的根文件,而不是主輸入樹的根。

+0

作品像魅力,比我的方法更優雅!謝謝一堆! – Solvemon

0

可能是這不是最簡潔的解決方案,但它的工作原理。

順便說一句,local-name返回一個沒有名稱空間的名字(即如果有冒號的話冒號後面的部分)。以防萬一你不知道這一點。

樣式

<?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:template match="/"> 
    <MsgHead> 
     <Document> 
      <Message> 
       <xsl:apply-templates/> 
      </Message> 
     </Document> 
    </MsgHead> 
</xsl:template> 

<xsl:template match="Message"> 
    <xsl:variable name="msg" select="."/> 
    <xsl:for-each select="document('default-nodes.xml')/DefaultNodes/*"> 
     <xsl:choose> 
      <xsl:when test="$msg/*/name()=current()/name()"> 
       <xsl:copy-of select="$msg/*[name()=current()/name()]"/> 
      </xsl:when> 
      <xsl:otherwise> 
       <xsl:copy-of select="."/> 
      </xsl:otherwise> 
     </xsl:choose> 
    </xsl:for-each> 
</xsl:template> 
</xsl:stylesheet> 

輸出

<?xml version="1.0" encoding="UTF-8"?> 
<MsgHead> 
<Document> 
    <Message> 
    <a>123</a> 
    <b>-1</b> 
    <c>456</c> 
    <d>789</d> 
    </Message> 
</Document> 
</MsgHead> 
+0

感謝您的回覆。我已經有@IanRoberts的解決方案可以工作了,但是決定試試你的。您的解決方案的以下部分: '$ msg/*/name()= current()/ name()'給我錯誤'預期的位置步驟'。 – Solvemon

+0

它確實與Saxon 9.1和XSLT 2.0一起工作,所以也許這是您XSLT處理器的一個特點?但是,由於伊恩的解決方案正常工作,不要花太多時間考慮這一點。乾杯! –

0

該XSLT樣式表1.0 ...

<xsl:stylesheet 
    version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:esl="urn:schemas-microsoft-com:xslt" 
    exclude-result-prefixes="xsl esl" > 
<xsl:output indent="yes" omit-xml-declaration="yes" /> 
<xsl:strip-space elements="*" />  

<xsl:key name="kByName" match="*" use="local-name()" /> 

<xsl:variable name="DefaultNodes"> 
<DefaultNodes> 
    <a>-1</a> 
    <b>-1</b> 
    <c>-1</c> 
    <d>-1</d> 
</DefaultNodes> 
</xsl:variable> 

<xsl:template match="Message"> 
<MsgHead> 
    <Document> 
    <xsl:copy> 
    <xsl:variable name="union"> 
     <xsl:apply-templates select="* | esl:node-set($DefaultNodes)/DefaultNodes/*[local-name()]"/> 
    </xsl:variable> 
    <xsl:copy-of select="esl:node-set($union)/*[generate-id() = generate-id(key('kByName',local-name())[1])]" /> 
    </xsl:copy> 
    </Document> 
</MsgHead> 
</xsl:template> 

<xsl:template match="@*|node()"> 
    <xsl:copy> 
    <xsl:apply-templates select="@*|node()" /> 
    </xsl:copy> 
</xsl:template>  

</xsl:stylesheet> 

...當施加到該輸入文件...

<Message> 
    <a>123</a> 
    <c>456</c> 
    <d>789</d> 
</Message> 

... ...收率

<MsgHead> 
    <Document> 
    <Message> 
     <a>123</a> 
     <c>456</c> 
     <d>789</d> 
     <b>-1</b> 
    </Message> 
    </Document> 
</MsgHead> 

注意: 根據需要將esl命名空間更改爲http://exslt.org/common,具體取決於您的XSLT處理器。

相關問題