2010-10-08 72 views
1

我的問題是,在一些xml文件中存在元素,在另一個文件中不存在。 當元素存在時,它的值應該被改變。如果它不存在,應該添加它。XSLT:當存在元素時,添加它並更改值,否則添加具有值的元素

這裏是更好地瞭解一個例子:

<root> 
    <group> 
     <element1>SomeValue1</element1> 
     <element2>SomeValue2</element2> 
    </group> 
</root> 

比方說,我總是想部件1,element2的和元素3與價值觀Changed1,Changed2,Changed3。

它最終應該是這樣的:

<root> 
    <group> 
     <element1>Changed1</element1> 
     <element2>Changed2</element2> 
     <element3>Changed3</element3> 
    </group> 
</root> 

我能做些什麼來做到這一點?
感謝你在期待

丹尼斯

+0

是否元素的順序重要嗎?如果是這樣,是否有多個元素可能需要插入? – 2010-10-08 10:54:26

+0

我簡化了一下:組元素可以有各種其他元素。但順序並不重要,每個元素只應出現一次 – Dennis 2010-10-08 11:21:24

+0

好問題,+1。請參閱我的答案,以獲得一個通用解決方案,該解決方案允許將待修改元素的名稱和內容與XSLT代碼分開指定。 – 2010-10-08 14:31:53

回答

2

我不知道這是否是在世界上最優雅的解決方案,但我認爲這可能會做什麼你是後:

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

<!-- If the element exists, do what you want to do --> 
<xsl:template match="element1"> 
    <xsl:copy>Changed1</xsl:copy> 
</xsl:template> 

<xsl:template match="element2"> 
    <xsl:copy>Changed2</xsl:copy> 
</xsl:template> 

<xsl:template match="element3"> 
    <xsl:copy>Changed3</xsl:copy> 
</xsl:template> 

<!-- If the element doesn't exist, add it --> 
<xsl:template match="group"> 
    <xsl:copy> 
     <xsl:apply-templates/> 
     <xsl:if test="not(element1)"> 
      <element1>Changed1</element1> 
     </xsl:if> 
     <xsl:if test="not(element2)"> 
      <element2>Changed2</element2> 
     </xsl:if> 
     <xsl:if test="not(element3)"> 
      <element3>Changed3</element3> 
     </xsl:if> 
    </xsl:copy> 
</xsl:template> 

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

</xsl:stylesheet> 

任何未明確匹配的應該只是複製跨越不變。

當然,如果值是常數(即,部件1,element2的和元素3將始終具有相同的值,無論它們是否是新的或更新),那麼你可以有更簡單的東西:

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

<!-- If the element exists, remove it --> 
<xsl:template match="element1 | element2 | element3"/> 

<!-- Now put in your preferred elements --> 
<xsl:template match="group"> 
    <xsl:copy> 
     <xsl:apply-templates/> 
      <element1>Changed1</element1> 
      <element2>Changed2</element2> 
      <element3>Changed3</element3> 
    </xsl:copy> 
</xsl:template> 

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

</xsl:stylesheet> 

這基本上刪除了原來的「元素」節點,並將其置於其位置。

+0

適合我...非常感謝:-) – Dennis 2010-10-08 12:17:19

+0

+1第二個是一個很好的答案。另外,我編輯了您的身份規則:不需要將表達式拆分爲兩條指令,並且可以交換工會條款,因爲申請無論如何都會遵循文檔順序。 – 2010-10-08 12:41:59

+0

@Alejandro:您可能有興趣看到一個通用的解決方案:) – 2010-10-08 14:36:52

0

你的例子可能過於簡單。看起來你可以爲每個遇到的元素生成3個「改變」的值......所以我猜這比現實生活中的要複雜一點。

在任何情況下,如果改變後的值是不是替換,但實際上的原始值的修改,可以大概使用for-eachgroup秒,然後生成的3個元素,其中每個元素的值是基於xsl-if元素,該元素檢查原始值是否存在並且如果存在則使用它。

+0

好吧,它有點過於簡單了:-) – Dennis 2010-10-08 10:31:20

+0

對不起,按下輸入提前。 group元素可以包含各種其他元素,這些元素應該是未觸及的。 – Dennis 2010-10-08 10:32:17

0

這是一個更通用的解決方案。的元素的名稱可以分別從代碼來指定:

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

    <my:newValues> 
     <element1>Changed1</element1> 
     <element2>Changed2</element2> 
     <element3>Changed3</element3> 
    </my:newValues> 

    <xsl:variable name="vElements" select="document('')/*/my:newValues/*"/> 

<xsl:template match="*" name="identity" mode="copy"> 
    <xsl:element name="{name()}"> 
     <xsl:copy-of select="namespace::*[not(name()='my' or name()='xsl')]"/> 
     <xsl:apply-templates select="node()|@*"/> 
    </xsl:element> 
</xsl:template> 

<xsl:template match="*"> 
    <xsl:if test="not($vElements[name()=name(current())])"> 
    <xsl:call-template name="identity"/> 
    </xsl:if> 
</xsl:template> 

<xsl:template match="group"> 
    <xsl:copy> 
    <xsl:apply-templates select="node()"/> 
    <xsl:apply-templates select="$vElements" mode="copy"/> 
    </xsl:copy> 
</xsl:template> 
</xsl:stylesheet> 

當這個變換所提供的XML文檔施加:

<root> 
    <group> 
     <element1>SomeValue1</element1> 
     <element2>SomeValue2</element2> 
    </group> 
</root> 

有用,正確的結果產生

<root> 
    <group> 
     <element1>Changed1</element1> 
     <element2>Changed2</element2> 
     <element3>Changed3</element3> 
    </group> 
</root> 

:當要修改的元素位於單獨的文檔中時,不需要名稱空間的看似複雜的處理將丟棄名稱空間。爲了演示目的,該代碼有意將待修改的元素放入與xslt樣式表相同的文檔中。實際上,只會使用<xsl:copy-of select="$vModifiedDoc/*">

+0

+1是的,這是我一直在想的一般解決方案。很少有XSLT處理器處理XML NAMES 1.1是一種痛苦,因爲通過這種方式,您可以重置內聯數據中的命名空間,以獲得更緊湊的代碼。 – 2010-10-08 15:28:15

0

只是爲了好玩,一個XSLT 2.0解決方案:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:variable name="vAdd" as="element()*"> 
     <element1>Changed1</element1> 
     <element2>Changed2</element2> 
     <element3>Changed3</element3> 
    </xsl:variable> 
    <xsl:template match="node()|@*"> 
     <xsl:copy> 
      <xsl:apply-templates select="node()|@*"/> 
     </xsl:copy> 
    </xsl:template> 
    <xsl:template match="group/*[name()=$vAdd/name()]"/> 
    <xsl:template match="group/*[last()]" priority="1"> 
     <xsl:next-match/> 
     <xsl:apply-templates select="$vAdd"/> 
    </xsl:template> 
</xsl:stylesheet> 

輸出:

<root> 
    <group> 
     <element1>Changed1</element1> 
     <element2>Changed2</element2> 
     <element3>Changed3</element3> 
    </group> 
</root>