Change the value of an attribute according to the value of another attribute

P

patrizio.trinchini

Hi All,

I'would like to write an XSL transformation that changes the value of
the atribute of a given element according to the value of another
atttribute of the same element.

For instance, given the following XML file:

<?xml version="1.0" encoding="UTF-8"?>
<sample xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNameSpaceSchemaLocation="sample.xsd">
<parent-element>
<child-element name="AAA" value="XXX"/>
<child-element name="BBB" value="XXX"/>
</parent-element>
</sample>

I would obtain the following one:

<?xml version="1.0" encoding="UTF-8"?>
<sample xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNameSpaceSchemaLocation="sample.xsd">
<parent-element>
<child-element name="AAA" value="AAA_VALUE"/>
<child-element name="BBB" value="BBB_VALUE"/>
</parent-element>
</sample>

where the value of the 'value' attribute of the first child-element has
been changed to AAA_VALUE because the value of the 'name' attribute was
AAA; the value of the 'value' attribute of the second child-element has
been changed to BBB_VALUE because the value of the 'name' attribute was
AAA.

I wrote the following XSLT to do the job:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:eek:utput indent="yes"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="child-element[@name='AAA']">
<xsl:apply-templates select="@*[local-name() != 'value']" />
<xsl:attribute name="value">
<xsl:text>AAA_VALUE</xsl:text>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>

but it only handles the first child-element; I don't know how to handle
both the child-element, and possibly more then two, in a single
transform, is it possible? or maybe I need to chain multiple
transformations?

Thanks a lot for your help

Patrizio
 
T

toudidel

i don't understand what you want as the output of the transformation. get
some example of xml document and output xml document

td
 
M

Martin Honnen

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:eek:utput indent="yes"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="child-element[@name='AAA']">
<xsl:apply-templates select="@*[local-name() != 'value']" />
<xsl:attribute name="value">
<xsl:text>AAA_VALUE</xsl:text>
</xsl:attribute>
</xsl:template>

You could simply use the first template you have plus
<xsl:template match="child-element/@value">
<xsl:attribute name="{name()}"><xsl:value-of
select="concat(../@name, '_VALUE')"/></xsl:attribute>
</xsl:template>
 
P

patrizio.trinchini

Martin said:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:eek:utput indent="yes"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="child-element[@name='AAA']">
<xsl:apply-templates select="@*[local-name() != 'value']" />
<xsl:attribute name="value">
<xsl:text>AAA_VALUE</xsl:text>
</xsl:attribute>
</xsl:template>

You could simply use the first template you have plus
<xsl:template match="child-element/@value">
<xsl:attribute name="{name()}"><xsl:value-of
select="concat(../@name, '_VALUE')"/></xsl:attribute>
</xsl:template>

Thanks a lot for your suggestion, but maybe the sample XML was not
very self-explaining, may fault.
What I would obtain is indeed to transformt an XML like this:

<?xml version="1.0" encoding="UTF-8"?>
<sample xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNameSpaceSchemaLocation="sample.xsd">
<parent-element>
<child-element name="AAA" value="anOldValueForAAA"/>
<child-element name="BBB" value="anOldValueForBBB"/>
<child-element name="CCC" value="anOldValueForCCC"/>
...
</parent-element>
</sample>

Into another XML like the follwoing:

<?xml version="1.0" encoding="UTF-8"?>
<sample xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNameSpaceSchemaLocation="sample.xsd">
<parent-element>
<child-element name="AAA" value="aNewValue1"/>
<child-element name="BBB" value="aNewValue2"/>
<child-element name="CCC" value="aNewValue3"/>
...
</parent-element>
</sample>

whose content has been determined according to the following rules:

FOR EACH child-element
IF name == AAA THEN value = aNewValue1
ESLE IF name == BBB THEN value = aNewValue2
ESLE IF name == CCC THEN value = aNewValue3
...

Thanks again for your help

Regards,

Patrizio
 
M

Martin Honnen

whose content has been determined according to the following rules:

FOR EACH child-element
IF name == AAA THEN value = aNewValue1
ESLE IF name == BBB THEN value = aNewValue2
ESLE IF name == CCC THEN value = aNewValue3
...

I see no way then but hardcoding the different cases e.g.
<xsl:template match="child-element[@name = 'AAA']/@value">
<xsl:attribute name="{name()}">aNewValue1</xsl:attribute>
<xsl:template>

<xsl:template match="child-element[@name = 'BBB']/@value">
<xsl:attribute name="{name()}">aNewValue2</xsl:attribute>
<xsl:template>

<xsl:template match="child-element[@name = 'CCC']/@value">
<xsl:attribute name="{name()}">aNewValue3</xsl:attribute>
<xsl:template>
(plus the identity transformation template, the first one you already had).
 
P

Philippe Poulard

Martin said:
whose content has been determined according to the following rules:

FOR EACH child-element
IF name == AAA THEN value = aNewValue1
ESLE IF name == BBB THEN value = aNewValue2
ESLE IF name == CCC THEN value = aNewValue3
...


I see no way then but hardcoding the different cases e.g.
<xsl:template match="child-element[@name = 'AAA']/@value">
<xsl:attribute name="{name()}">aNewValue1</xsl:attribute>
<xsl:template>

<xsl:template match="child-element[@name = 'BBB']/@value">
<xsl:attribute name="{name()}">aNewValue2</xsl:attribute>
<xsl:template>

<xsl:template match="child-element[@name = 'CCC']/@value">
<xsl:attribute name="{name()}">aNewValue3</xsl:attribute>
<xsl:template>
(plus the identity transformation template, the first one you already had).

here is a better strategy : a kind of associative array

<data:values>
<val name="AAA">aNewValue1</val>
<val name="BBB">aNewValue2</val>
<val name="CCC">aNewValue3</val>
</data:values>
<xsl:template match="child-element/@value">
<xsl:attribute name="{name()}">
<xsl:value-of
select="document()/*/data:values/val[@name=current()/../@name]"/>
</xsl:attribute>
<xsl:template>

I did it quickly, I hope that there is an "apply template" on the
attribute, otherwise those defined above won't be invoked

Don't miss the namespace declaration for the prefix "data" : XSLT
requires a prefix name at this place of the stylesheet

--
Cordialement,

///
(. .)
--------ooO--(_)--Ooo--------
| Philippe Poulard |
-----------------------------
http://reflex.gforge.inria.fr/
Have the RefleX !
 
R

Richard Tobin

Martin Honnen said:
I see no way then but hardcoding the different cases e.g.
<xsl:template match="child-element[@name = 'AAA']/@value">
<xsl:attribute name="{name()}">aNewValue1</xsl:attribute>
<xsl:template>

You could use a table. To do this you could use a couple of less
commonly used features of XSLT:

- you can put any elements in a stylesheet provided that they
are in some namespace other than the XSLT namespace;
- you can refer to the stylesheet itself in a call to the document
function by using the same-document URI "".

So you could put elements like this at the top-level of the stylesheet:

<x:new name="AAA" value="aNewValue1"/>
<x:new name="BBB" value="aNewValue2"/>
...

(you'll have to declare the prefix "x" of course, with some namespace
name of your own choosing).

Then you can look up values in this table by using the document function:

<xsl:template match="child-element/@value">
<xsl:copy-of select="document('')//x:new[@name=current()/../@name]/@value"/>
</xsl:template>

-- Richard
 
M

Martin Honnen

You could use a table.
<x:new name="AAA" value="aNewValue1"/>
<x:new name="BBB" value="aNewValue2"/>

Yes, Philippe has already suggested the same approach. Nevertheless you
have to hardcode the different cases in the stylesheet as there seemed
to be no way to infer the new value from the old one.
 
P

patrizio.trinchini

Martin said:
Yes, Philippe has already suggested the same approach. Nevertheless you
have to hardcode the different cases in the stylesheet as there seemed
to be no way to infer the new value from the old one.

Thanks a lot to everyone!

the hard-coded solution works fine and is enough for what I need at
this time; also the associative table solution is very valuable and
I'll add it to my toolbox for the future... when I'll be more
proficient with XSLT ;-)

I've only another quick question

Is it possible to remove one or more elements according to the value of
an element's attribute; i.e. given the following source XML file:

<?xml version="1.0" encoding="UTF-8"?>
<sample xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNameSpaceSchemaLocation="sample.xsd">
<parent-element>
<child-element name="AAA" value="XXX"/>
<child-element name="BBB" value="XXX"/>
<child-element name="CCC" value="XXX"/>
<additional-data type="text"/>
<additional-info type="text"/>
</parent-element>
</sample>

is it possible, changing somewhat the last leg to the above IF - ELSE
IF switch:

...
ELESE IF name = 'CCC' THEN {
value = aNewValue3
remove elements additional-data and additional-info type
}

to obtain the following result:

<?xml version="1.0" encoding="UTF-8"?>
<sample xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNameSpaceSchemaLocation="sample.xsd">
<parent-element>
<child-element name="AAA" value="aNewValue1"/>
<child-element name="BBB" value="aNewValue2"/>
<child-element name="CCC" value="aNewValue3"/>
</parent-element>
</sample>

Thanks again for all your support

Regards,

Patrizio
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,968
Messages
2,570,154
Members
46,702
Latest member
LukasConde

Latest Threads

Top