2014-01-29 23 views
1

我想處理一個XML文件,我知道某些具有特定屬性的元素應該是新元素的子元素。其他不匹配的元素的其他兄弟應該保持不變。一個例子會更好。將匹配的元素添加到新元素,同時保持那些不匹配,因爲它們是

我想匹配的令牌具有屬性id ='t_15'和id ='t_16'。

這是輸入XML:

<text> 
    <div id="d_1"> 
    <p id="p_1"> 
     <s id="s_1"> 
     <token id="t_13" lemma="colleague" pos="NN">Colleagues</token> 
     <token id="t_14" lemma="," pos=",">,</token> 
     <token id="t_15" lemma="we" pos="PP">we</token> 
     <token id="t_16" lemma="now" pos="RB">now</token> 
     <token id="t_17" lemma="come" pos="VVP">come</token> 
     <token id="t_18" lemma="to" pos="TO">to</token> 
     <token id="t_19" lemma="catch-the-eye" pos="NN">catch-the-eye</token> 
     <token id="t_20" lemma="." pos="SENT">.</token> 
     </s> 
     <s id="s_2"> 
     <token id="t_21" lemma="these" pos="DT">These</token> 
     <token id="t_22" lemma="be" pos="VBP">are</token> 
     <token id="t_23" lemma="the" pos="DT">the</token> 
     <token id="t_24" lemma="name" pos="NNS">names</token> 
     <token id="t_25" lemma="I" pos="PP">I</token> 
     <token id="t_26" lemma="will" pos="MD">will</token> 
     <token id="t_27" lemma="call" pos="VV">call</token> 
     <token id="t_28" lemma="out" pos="RP">out</token> 
     <token id="t_29" lemma="." pos="SENT">.</token> 
     </s> 
    </p> 
    </div> 
</text> 

這是我希望得到的輸出:

<text> 
    <div id="d_1"> 
    <p id="p_1"> 
     <s id="s_1"> 
     <token id="t_13" lemma="colleague" pos="NN">Colleagues</token> 
     <token id="t_14" lemma="," pos=",">,</token> 
     <e> 
      <token id="t_15" lemma="we" pos="PP">we</token> 
      <token id="t_16" lemma="now" pos="RB">now</token> 
     </e> 
     <token id="t_17" lemma="come" pos="VVP">come</token> 
     <token id="t_18" lemma="to" pos="TO">to</token> 
     <token id="t_19" lemma="catch-the-eye" pos="NN">catch-the-eye</token> 
     <token id="t_20" lemma="." pos="SENT">.</token> 
     </s> 
     <s id="s_2"> 
     <token id="t_21" lemma="these" pos="DT">These</token> 
     <token id="t_22" lemma="be" pos="VBP">are</token> 
     <token id="t_23" lemma="the" pos="DT">the</token> 
     <token id="t_24" lemma="name" pos="NNS">names</token> 
     <token id="t_25" lemma="I" pos="PP">I</token> 
     <token id="t_26" lemma="will" pos="MD">will</token> 
     <token id="t_27" lemma="call" pos="VV">call</token> 
     <token id="t_28" lemma="out" pos="RP">out</token> 
     <token id="t_29" lemma="." pos="SENT">.</token> 
     </s> 
    </p> 
    </div> 
</text> 

這是我使用的樣式表:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="1.0"> 
    <xsl:output omit-xml-declaration="yes" indent="no"></xsl:output> 

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

    <xsl:variable name="dif_tok"> 
     <dif_tok>t_15</dif_tok> 
     <dif_tok>t_16</dif_tok> 
    </xsl:variable> 

    <xsl:template match="s"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*"></xsl:apply-templates> 
      <e> 
       <xsl:copy-of select="*[@id = $dif_tok/dif_tok]"></xsl:copy-of> 
      </e> 
     </xsl:copy> 
    </xsl:template> 

</xsl:stylesheet> 

這是,最後我得到的結果:

<text> 
    <div id="d_1"> 
    <p id="p_1"> 
     <s id="s_1"><e><token id="t_15" lemma="we" pos="PP">we</token><token id="t_16" lemma="now" pos="RB">now</token></e></s> 
     <s id="s_2"><e/></s> 
    </p> 
    </div> 
</text> 
+0

對不起,儘管在原文中我使用了XSLT 2.0聲明(我剛剛編輯過它)。我更喜歡XSLT 1.0解決方案。我想使用lxml將它集成到python腳本中,該腳本僅支持XSLT 1.0 –

+0

我試過Ian的解決方案,它正是我在尋找的,但它是XSLT 2.0。現在,我再也看不到他的建議,但它非常好。 –

+0

下次您提問時,請確保您使用正確的版本。這很令人討厭,我不得不說。 –

回答

0

具有特定屬性的一些元素應該是一個新元素的兒童

寫一個單獨的模板到token元素你有興趣相匹配。

<xsl:template match="token[@id ='t_15']"> 

在模板中,生成一個e元素並利用apply-templates的行爲來填充它的標記,其中@id = t_15 and t_16。然後,讓身份轉換完成剩下的工作。

樣式表

<?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="@*|node()"> 
     <xsl:copy> 
     <xsl:apply-templates select="@*|node()"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="token[@id ='t_15']"> 
     <e> 
     <xsl:copy> 
      <xsl:apply-templates select="@*|node()"/> 
     </xsl:copy> 
     <xsl:apply-templates select="following-sibling::token[1]"/> 
     </e> 
    </xsl:template> 

</xsl:stylesheet> 

輸出

<?xml version="1.0" encoding="UTF-8"?> 
<text> 
    <div id="d_1"> 
     <p id="p_1"> 
     <s id="s_1"> 
      <token id="t_13" lemma="colleague" pos="NN">Colleagues</token> 
      <token id="t_14" lemma="," pos=",">,</token> 
      <e> 
       <token id="t_15" lemma="we" pos="PP">we</token> 
       <token id="t_16" lemma="now" pos="RB">now</token> 
      </e> 
      <token id="t_16" lemma="now" pos="RB">now</token> 
      <token id="t_17" lemma="come" pos="VVP">come</token> 
      <token id="t_18" lemma="to" pos="TO">to</token> 
      <token id="t_19" lemma="catch-the-eye" pos="NN">catch-the-eye</token> 
      <token id="t_20" lemma="." pos="SENT">.</token> 
     </s> 
     <s id="s_2"> 
      <token id="t_21" lemma="these" pos="DT">These</token> 
      <token id="t_22" lemma="be" pos="VBP">are</token> 
      <token id="t_23" lemma="the" pos="DT">the</token> 
      <token id="t_24" lemma="name" pos="NNS">names</token> 
      <token id="t_25" lemma="I" pos="PP">I</token> 
      <token id="t_26" lemma="will" pos="MD">will</token> 
      <token id="t_27" lemma="call" pos="VV">call</token> 
      <token id="t_28" lemma="out" pos="RP">out</token> 
      <token id="t_29" lemma="." pos="SENT">.</token> 
     </s> 
     </p> 
    </div> 
</text> 

你嘗試的解決方案採用的是可變的。這也是一個好方法:

<xsl:variable name="start" select="'t_15'"/> 

<xsl:template match="token[@id=$start]"> 
    <e> 
    <xsl:copy> 
     <xsl:apply-templates select="@*|node()"/> 
    </xsl:copy> 
    <xsl:apply-templates select="following-sibling::token[1]"/> 
    </e> 
</xsl:template> 
+0

感謝Matthias!爲了使它更加靈活,讓我問你一些問題:我想附加到元素「」的標記跨度可以有任意長度,比如1個標記或n個標記。你會怎麼做?我可以向樣式表傳遞一個帶有第一個標識id和要處理的標記的長度的參數,或者最後一個標記的標識。比如說,我只想處理t_15或t_15,t_16和t_17,而不僅僅是t_15和t_16。最好! –

+0

' now'出現了兩次輸出。 –

+0

@ JoelM.Lamsen:好點。這是一個不太好的副作用。 –

0

我稍微修改了Mathias製作的樣式表。見下文。

<?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="@*|node()"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*|node()"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:variable name="start" select="'t_15'"/> 
    <xsl:variable name="middle" select="'t_16'"/> 
    <xsl:variable name="end" select="'t_17'"/> 

    <xsl:template match="token[@id =$start]"> 
     <e> 
      <xsl:copy> 
       <xsl:apply-templates select="@*|node()"/> 
      </xsl:copy> 
      <xsl:apply-templates select="following-sibling::token[@id=$middle]" mode="group"/> 
      <xsl:apply-templates select="following-sibling::token[@id=$end]" mode="group"/> 
     </e> 
    </xsl:template> 

    <xsl:template match="token[@id =$middle]|token[@id =$end]" mode="group"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*|node()"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="token[@id =$middle]|token[@id =$end]"/> 

</xsl:stylesheet> 
0

編輯:因爲我寫這個答案的問題已經變爲需要XSLT 1.0,而不是2.0,但我會離開這裏的答案以供參考。


既然你在XSLT 2.0是這是對group-adjacent機制for-each-group理想的情況下。假設你像以前那樣有身份模板,然後怎麼樣

<xsl:variable name="tokensToGroup" select="('t_15', 't_16')" /> 

<xsl:template match="s"> 
    <xsl:copy> 
    <xsl:apply-templates select="@*" /> 
    <xsl:for-each-group select="token" group-adjacent="@id = $tokensToGroup"> 
     <xsl:choose> 
     <xsl:when test="current-grouping-key()"> 
      <e><xsl:apply-templates select="current-group()" /></e> 
     </xsl:when> 
     <xsl:otherwise> 
      <xsl:apply-templates select="current-group()" /> 
     </xsl:otherwise> 
     </xsl:choose> 
    </xsl:for-each-group> 
    </xsl:copy> 
</xsl:template> 

相鄰的標記,其ID匹配$tokensToGroup將被收集到一個單一組的布爾true一個current-grouping-key()值,其他標記將聚集成團用鑰匙false - 我們只用true鑰匙包裝<e>...</e>

如果不是所有的ID分組列表,你就必須在第一和最後一個令牌的ID,那麼你可以使用相同的機制,但具有不同的分組鍵定義

<xsl:param name="first" select="'t_15'" /> 
<xsl:param name="last" select="'t_17'" /> 

<xsl:for-each-group select="token" 
    group-adjacent="((. | preceding-sibling::token)[@id = $first] and 
        (. | following-sibling::token)[@id = $last])"> 

這裏true關鍵值是$firstToken處於或等於$lastToken之前或之後的元素。假設處理器尚未優化它,這可能對於一個密鑰更高效一些,例如

<xsl:key name="tokenById" match="token" use="@id" /> 

<xsl:variable name="firstToken" select="key('tokenById', $first)" /> 
<xsl:variable name="lastToken" select="key('tokenById', $last)" /> 

<xsl:for-each-group select="token" 
    group-adjacent="((. is $firstToken or . &gt;&gt; $firstToken) and 
        (. is $lastToken or . &lt;&lt; $lastToken))">