Identity transformation problems

B

Bilal

Hello,
I'm attempting to "populate" an template XML file with some data using
identity transform (approach suggested by Joe K.; Thanks! :) and have
come across some strange behaviour using XMLSpy, AltovaXML, and
xsltproc. The actual stylesheets are to be auto-generated by another
stylesheet (that process I've worked out! :) and now troubleshooting
these generated stylesheets in XMLSpy's XSL debugger. So I'm wondering
if my approach is basically wrong to elicit the unexpected behaviour,
rather than that the apps are buggy. So here goes:

The transformation stylesheet is:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/message/Control/Trace/Id">
<xsl:attribute name="extension">bbb-999</xsl:attribute>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

nothing fancy except that I'm trying to put/add value to the
"/message/Control/Trace/Id/@extension" attribute and the xml to be
transformed is:

<?xml version="1.0" encoding="ISO-8859-1"?>
<message>
<id>Text</id>
<creationTime>Text</creationTime>
<versionCode/>
<Id>Text</Id>
<processingCode/>
<processingModeCode/>
<acceptCode/>
<commRcv>
<device>
<id extension="Old_Ext">Text</id>
</device>
</commRcv>
<commSnd>
<device>
<id>Text</id>
</device>
</commSnd>
<Control>
<author>
<Entity>
<id>Text</id>
<code/>
<Organization>
<id>Text</id>
</Organization>
</Entity>
</author>
<Trace>
<Id extension="OLD">Text</Id>
<person>
<value/>
<Text>String</Text>
</person>
<birthTime>
<value>Text</value>
<Text>String</Text>
</birthTime>
<name>
<value>Text</value>
<Text>String</Text>
</name>
</Trace>
</Control>
</message>

I'm unsure if I am doing anything illegal as far as XSL/T etc. is
concerned but here are the results:

XMLSpy + AltovaXML add the attribute to /message/Control/Trace instead,
and the /message/Control/Trace/Id element isn't created at all.

xsltproc:
Gives the error "xsl:attribute: Cannot add attributes to an element if
children have been already added to the element." That is very odd since
the /message/Control/Trace/Id/ element doesn't have any children!

I've tried various permutations of the stylesheet as follows:

Trial #1: Populate attribute & element
<xsl:attribute name="extension">bbb-999</xsl:attribute>
<xsl:elementname="Id">xsl-123456789</xsl:element>

XMLSpy/AltovaXML add the extension to the parent /message/Control/Trace
element and the "Id" element isn't created. xsltproc errors as before.

Trial #2: Populate element & attribute
<xsl:elementname="Id">xsl-123456789</xsl:element>
<xsl:attribute name="extension">bbb-999</xsl:attribute>

XMLSpy/AltovaXML add/populate the "Id" element correctly but the
"extension" attribute is missing. xsltproc errors as before.

Trial #3: Populate element
<xsl:elementname="Id">xsl-123456789</xsl:element>

Value for "Id" element is add without problem by all.

Trial #4: Two seperate templates
This was my original approach but didn't work (see below). Created two
seperate template, one for the element and one for the attribute:

<xsl:template match="/message/Control/Trace/Id/@extension">
<xsl:attribute
name="extension">bbb-999</xsl:attribute> </xsl:template>
<xsl:template match="/message/Control/Trace/Id">
<xsl:element name="queryId">xsl-123456789</xsl:element>
</xsl:template>

The template for the "Id" element is actioned while the one for the
"Id/@extension" attribute never is; unsure why the xslt processor never
'runs' the template for the


So what am I doing wrong??? Is there a better/proper way to populate
both the element and its attribute?

Would aoppreciate any suggestions! :)

Regards,

Bilal B.
 
M

Martin Honnen

Bilal wrote:

<xsl:template match="/message/Control/Trace/Id">
<xsl:attribute name="extension">bbb-999</xsl:attribute>
</xsl:template>

That does not work out, if you want to add an attribute to that element
then you need to first copy the element

<xsl:template match="/message/Control/Trace/Id">
<xsl:copy>
<!-- use that if you have other attributes to copy -->
<xsl:apply-templates select="@*"/>
<xsl:attribute name="extension">bbb-999</xsl:attribute>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
 
B

Bilal

Hi Martin,
Many thanks!! That works for adding/setting the attribute but what if
the element's value is also being set simultaneously?

Regards,

Bilal
 
M

Martin Honnen

Bilal wrote:

That works for adding/setting the attribute but what if
the element's value is also being set simultaneously?

Well once you write a template for that element it is up to you to fill
the template with instructions as needed, the xsl:copy will only copy
the element itself, the <xsl:apply-templates select="@*"/> (and the
other template you had) take care of copying existing attributes, the
<xsl:attribute name="extension">bbb-999</xsl:attribute> adds the new
attribute, and the <xsl:apply-templates/> lets the XSLT processor
process the existing child nodes. If you don't want any of those steps
then remove or replace them e.g.

<xsl:template match="/message/Control/Trace/Id">
<xsl:copy>
<!-- use that if you have other attributes to copy -->
<xsl:apply-templates select="@*"/>
<xsl:attribute name="extension">bbb-999</xsl:attribute>
<xsl:text>Kibology for all</xsl:text>
</xsl:copy>
</xsl:template>

will not process the child nodes but simply add that (example) text as a
child node.
 
B

Bilal

Hi Martin,
Thanks for the explanation and the example. Unfortunately, I still
don't have a full understanding about the 'precedence' of how the
templates are applied, and hence in my not-so-infiite wisdom, I was
trying to use the code below (note the xsl:element tag instead of the
xsl:text tag) to put value in the Id element itself, and kept on
wondering why it was adding an new element /message/Control/Trace/Id/Id
instead :-(

<xsl:template match="/message/Control/Trace/Id">
<xsl:copy>
<!-- use that if you have other attributes to copy -->
<xsl:apply-templates select="@*"/>
<xsl:attribute name="extension">bbb-999</xsl:attribute>
<xsl:element name="Id">Kibology for all</xsl:element>
</xsl:copy>
</xsl:template>

Just to take this one step further, what would be the right way to copy
the children below /message/Control/Trace/Id using the default template?

<xsl:template match="/message/Control/Trace/Id">
<xsl:copy>
<!-- use that if you have other attributes to copy -->
<xsl:apply-templates select="@*"/>
<xsl:attribute name="extension">bbb-999</xsl:attribute>
<xsl:text>Kibology for all</xsl:text>
<xsl:apply-templates />
</xsl:copy>
</xsl:template>

This appears to do the trick (I think) but what it ALSO does is copy the
old text value (child?) AFTER inserting the new text value, as well as
copying any remaining child elements.
How would one achieve copying the new text value/child, avoid copying
the old text value/child, but copy any child elements?

Lastly, any recommendations on good XSL resources/books other then
obvious ones on the web? I think I'm reaching the limits of my
self-learned XSLT knowledge.

Regards,

Bilal B.
 
M

Martin Honnen

Bilal wrote:

Unfortunately, I still
don't have a full understanding about the 'precedence' of how the
templates are applied, and hence in my not-so-infiite wisdom, I was
trying to use the code below (note the xsl:element tag instead of the
xsl:text tag) to put value in the Id element itself, and kept on
wondering why it was adding an new element /message/Control/Trace/Id/Id
instead :-(

<xsl:template match="/message/Control/Trace/Id">

You have a template here for Id elements with parent Trace with parent
Control with parent message as the root element, then in that template
you do
<xsl:copy>

meaning that Id element is copied. If you don't want to copy those Id
elements then don't use xsl:copy in this template. But if you want to
have attributes like that extension attribute you first need an element
to add it to.

How would one achieve copying the new text value/child, avoid copying
the old text value/child, but copy any child elements?

Sorry, in my understanding you can't "copy" "new" values or children,
"new" stuff can only be added.
As for copying only child _elements_ but not child _text_ nodes simply
do e.g.
<xsl:apply-templates select="*"/>
instead of
<xsl:apply-templates/>
in the template where you want to do that. For the rare case that you
also have comment nodes or processing instruction nodes that need to be
copied like the elements then do e.g.
<xsl:apply-templates select="node()[self::* or self::comment() or
self::processing-instruction()]"/>
 
J

Joe Kesselman

Bilal said:
don't have a full understanding about the 'precedence' of how the
templates are applied

There are almost no precedence rules, unfortunately. If you've got a
possible conflict, you should probably make your precedence explicit by
adding the priority attribute to the templates in question.
How would one achieve copying the new text value/child, avoid copying
the old text value/child, but copy any child elements?

One solution: Change the select in the second apply-templates so it
matches only elements. "*" will work, since the implied axis is child
and the principal node type of that axis is element. If you wanted to
accept other kinds of non-text nodes you might want to enhance that a
bit, eg "*|comment()".

Another solution would be to use a mode, but that's a bit more
complicated to explain if you aren't already familiar with them.
Lastly, any recommendations on good XSL resources/books other then
obvious ones on the web? I think I'm reaching the limits of my
self-learned XSLT knowledge.

I haven't looked at what's come out recently. Mike Kay's tome is still
the best XSLT reference manual out there, I think, but if you're looking
for a tutorial you'll want something else as well.
 
B

Bilal

Hi Joe and Martin,
Thanks for the replies; your explanation/examples helped solve the
problem I had been stuck!

Many thanks! :)

Regards,

Bilal B.
 

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

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top