2010-10-08 38 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>