开发者

XSLT: When element exists add it and change value otherwise add element with value

My problem is that in some xml files an element ex开发者_高级运维ists and in another it does not. When the element exists, it's value should be changed. If it does not exists, it should be added.

Here is an example for better understanding:

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

Let's say I always want element1, element2 and element3 with the values Changed1, Changed2, Changed3.

It should end up like this:

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

What can I do to make it happen?

Thanking you in anticipation

Dennis


I'm not sure if it's the most elegant solution in the world, but I think this would probably do what you're after:

<?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>

Anything which isn't explicitly matched should just copy across untouched.

Of course, if the values are constant (that is, element1, element2 and element3 will always have the same values regardless of whether they're new or updated) then you can have something simpler:

<?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>

Which essentially removes the original "element" nodes and puts yours in in their place.


Here is a more generic solution. The names of the elements can be specified separately from the code:

<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>

when this transformation is applied on the provided XML document:

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

the wanted, correct result is produced:

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

Note: The seemingly complicated processing that discards the "xsl" anf "my" namespaces is unnecessary when the to-be-modified elements are in their separate document. This code intentionally puts the to-be-modified elements in the same document as the xslt stylesheet for demonstration purposes. In practice, just an <xsl:copy-of select="$vModifiedDoc/*"> will be used.


Your example might be oversimplified. It looks like you can just generate the 3 "changed" values for every element you encounter... so I'm guessing it's a bit more complicated than that in real life.

In any case, if the changed value is not a replacement but actually a modification of the original value, you can probably use a for-each on groups and then generate the 3 elements, where the value of each element is based on an xsl-if element that checks whether the original value exists and uses it if so.


Just for fun, an XSLT 2.0 solution:

<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>

Output:

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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜