Matching the location of an element in a secondary file

Here should go questions about transforming XML with XSLT and FOP.
pjl
Posts: 12
Joined: Tue May 16, 2006 1:16 pm

Matching the location of an element in a secondary file

Post by pjl »

When processing a particular element in the input XML file, e.g.

<xsl:apply-tempate match="Feature">
...
</....>

I want to test whether that specific element(identified by an attribute value) exists at the same location, relative to the root element, in a second file. The root elements of the input and second XML documents have different names.

So given input.xml.
<Input>
<Feature name="featureA"/>
<Blah>
<Feature name="featurB"/>
</Blah></Input>

and a second XML file.

<Second>
<Blah>
<Feature name="featureB"/>
</Blah></Second>

I would expect a match on featureB but not on featureA. I plan to use a conditional test to output differences to an output XML file.

I could envisage obtaining a string representation of the ancestor nodes and then passing this off to a function to process (not that I know how to do this). Any ideas of how I might do this? Thanks. Peter.
george
Site Admin
Posts: 2095
Joined: Thu Jan 09, 2003 2:58 pm

Post by george »

Hi,

Well, in your samples the second file root is different. Now assuming as input

first.xml

Code: Select all


<Input>
<Feature name="featureA"/>
<Blah>
<Feature name="featurB"/>
</Blah>
</Input>
and second.xml

Code: Select all


<?xml version="1.0" encoding="UTF-8"?>
<Input>
<Blah>
<Feature name="featureB"/>
</Blah>
</Input>
I would solve this with a two stage transformation like below.

stage1.xsl applied on first.xml gives stage2.xsl

stage1.xsl

Code: Select all


<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:x="aliasOfXSLTNamespace">
<xsl:namespace-alias stylesheet-prefix="x" result-prefix="xsl"/>

<xsl:template match="/">
<x:stylesheet version="1.0">
<x:template match="/">
<result>
<xsl:apply-templates/>
</result>
</x:template>
</x:stylesheet>
</xsl:template>

<xsl:template match="Feature">
<xsl:variable name="path">
<xsl:for-each select="ancestor-or-self::*">
<xsl:value-of select="name()"/>
<xsl:if test="position()!=last()">
<xsl:text>/</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<x:if test="{$path}">
<x:text>Got: <xsl:value-of select="$path"/></x:text>


</x:if>
</xsl:template>
</xsl:stylesheet>
stage2.xsl is generated by the above transformation and in this case is

Code: Select all


<?xml version="1.0" encoding="utf-8"?>
<x:stylesheet xmlns:x="http://www.w3.org/1999/XSL/Transform"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<x:template match="/">
<result>
<x:if test="Input/Feature">
<x:text>Got: Input/Feature</x:text>
</x:if>

<x:if test="Input/Blah/Feature">
<x:text>Got: Input/Blah/Feature</x:text>
</x:if>

</result>
</x:template>
</x:stylesheet>
then apply stage2.xsl on second.xml and that gives

Code: Select all


<?xml version="1.0" encoding="utf-8"?>
<result>Got: Input/Blah/Feature</result>
Best Regards,
George
pjl
Posts: 12
Joined: Tue May 16, 2006 1:16 pm

Thanks with slight update

Post by pjl »

Thanks for the post.
Forgot to mention though that the root element ONLY can and will be differnt.
pjl
Posts: 12
Joined: Tue May 16, 2006 1:16 pm

and also...

Post by pjl »

I don't want to hard code any paths in the solution.

Cheers,

Peter.
george
Site Admin
Posts: 2095
Joined: Thu Jan 09, 2003 2:58 pm

Post by george »

Hi Peter,

Then exclude the root element from the test and have the generated template match on the root element instead of the document node:

Code: Select all


<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:x="aliasOfXSLTNamespace">
<xsl:namespace-alias stylesheet-prefix="x" result-prefix="xsl"/>
<xsl:output indent="yes"/>
<xsl:template match="/">
<x:stylesheet version="1.0">
<x:template match="/*">
<result>
<xsl:apply-templates/>
</result>
</x:template>
</x:stylesheet>
</xsl:template>

<xsl:template match="Feature">
<xsl:variable name="path">
<xsl:for-each select="ancestor-or-self::*[generate-id(.)!=generate-id(/*)]">
<xsl:value-of select="name()"/>
<xsl:if test="position()!=last()">
<xsl:text>/</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<x:if test="{$path}">
<x:text>Got: <xsl:value-of select="$path"/></x:text>


</x:if>
</xsl:template>
</xsl:stylesheet>
then the generated stylesheet will look like

Code: Select all


<?xml version="1.0" encoding="utf-8"?>
<x:stylesheet xmlns:x="http://www.w3.org/1999/XSL/Transform"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<x:template match="/*">
<result>
<x:if test="Feature">
<x:text>Got: Feature</x:text>
</x:if>
<x:if test="Blah/Feature">
<x:text>Got: Blah/Feature</x:text>
</x:if>
</result>
</x:template>
</x:stylesheet>
and will give you the result no matter what the root element is in the second XML file.
There is nothing hardcoded.

Best Regards,
George
pjl
Posts: 12
Joined: Tue May 16, 2006 1:16 pm

cheers

Post by pjl »

Cheers, I'll check it out when I get back from my hols......
Post Reply