3

I would like to modify an attribute of a very long xml like this:

<element index="0">
    <subelement bla="asdf" />
    <subelement bla="asdf" />
</element>
<element index="1">
    <subelement bla="asdf" />
    <subelement bla="asdf" />
</element>
...

I need to add N the value of each index attribute. Say N=5. The result would be:

<element index="5">
    <subelement bla="asdf" />
    <subelement bla="asdf" />
</element>
<element index="6">
    <subelement bla="asdf" />
    <subelement bla="asdf" />
</element>
...

What's the easiest way to do this? I presume it would be with XSLT, but I don't know how to do it.

Thanks!

3 Answers 3

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

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

  <!-- except "element" nodes -->
  <xsl:template match="element">
    <xsl:copy>
      <xsl:attribute name="index">
        <xsl:value-of select="@index + 5"/>
      </xsl:attribute>
      <xsl:apply-templates />
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>
Sign up to request clarification or add additional context in comments.

Comments

3

svick answer it's a good solution. Just in case you have to keep exact structure, try:

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

<xsl:param name="increment" select="5" />

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

<xsl:template match="node()[not(node())]">
 <xsl:element name="{name()}" >
  <xsl:apply-templates select="@*" />
 </xsl:element>
</xsl:template>

<xsl:template match="element/@index">
 <xsl:attribute name="index">
  <xsl:value-of select="$increment + ." />
 </xsl:attribute>
</xsl:template>

</xsl:stylesheet>

2 Comments

Matching element/@index directly is actually a better solution than mine (it works even if <element> has other attributes). But why do you have the second <template> for empty elements?
In some rare cases (eg XHTML) is important to distinguish between the two possible forms of empty elements ("<element />" or "<element></element>"). As "xsl:copy" does not make this distinction, it is necessary to use "xsl:element".
0

Here's a somewhat general solution for an arbitrary attribute to increase:

#!/bin/bash

PROG=$(basename $0 )
: ${TMPDIR:=/tmp}
:  ${INC:=1}
TMPFILE=$TMPDIR/$PROG.$$.xml

function usage() {

cat <<!
$PROG <attribute>  <input-xml>

Increases all attributes named <attribute> in the <input-xml> XML file by 1 ( or \$INC ) 
!
exit 1
}

[ $# -eq 2 ] || usage

{
cat <<!
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="xml" indent="yes"/>

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

  <!-- except "attribute-of-interest" node -->
  <xsl:template match="@$1">
    <xsl:attribute name="$1">
        <xsl:value-of select=". + $INC"/>
    </xsl:attribute>
  </xsl:template>

</xsl:stylesheet>
!
}  xsltproc /dev/stdin $2 > $TMPFILE  && mv $TMPFILE $2

Clearly this is svick's answer. I changed the XSL somewhat to cover multiple attributes in the target element. FWIW, I templatized it using bash.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.