Any simple way to obtain a "dynamically-defined" nodeset?

Y

Yarik

Hello,

I am not sure the subject of my post adequately describes the problem I
am trying to solve, so I think a specific example would be helpful.

Let's say there are XML descriptions of products like this one:

<!-- File: Products.xml -->
...
<Product id="p1">
<Title>....</Title>
...
<PricingInfo>
<RegularPrice>100</RegularPrice>
<Discount>25</Discount>
...
</PricingInfo>
...
<ShippingInfo>
<Cost>9.95</Cost>
<MaxDelay>1 business day</MaxDelay>
...
</ShippingInfo>
...
</Product>

The key thing here is that the schema of <Product> is not "flat" - it
contains elements with subelements, which may also have subelements,
and so on...

Now, let's say that I want to have an XML file that describes some sort
of an HTML page template which, in particular, refers to certain
attributes of <Product>s. Something along these lines:

<!-- File: PageTemplate.xml -->
...
<section>General</section>
<p>Title: <ProductProperty rxpath="Title"/></p>
<section>Shipping Info</section>
<p>Cost: <ProductProperty rxpath="ShippingInfo/Cost"/></p>
<p>Delay: <ProductProperty rxpath="ShippingInfo/Delay"/></p>
...

Finally, in an XSL file, whose templates are supposed to be applied to
PageTemplate.xml, I would like to write something like this:

<!-- File: ProductPage.xsl -->
...
<xs:template match="ProductProperty">
<!-- The parameter contains a nodeset from Products.xml -->
<!-- Or there could be a variable with the same contents... -->
<xs:param name="nsProduct" tunnel="yes"/>
<!-- And below is where the fun begins... -->
<xs:apply-templates
select="$nsProduct/*[name()=current()/@rxpath]"/>
</xs:template>

The problem is: I do not know what could I specify the @select
attribute of the <xs:apply-templates> element to retrieve exactly the
sub-nodeset of <Product> element defined by the @rxpath attribute of
the <ProductProperty> element.

The expression I wrote above -- $nsProduct/*[name()=current()/@rxpath]
-- works fine for @rxpaths that specify direct children of the
<Product> element (like "Title"), but obviously does not work for any
ancestor elements (like "ShippingInfo/Cost"). So I guess I am looking
for a way to dynamically obtain a sub-nodeset of a specified "origin
nodeset" (in my example, it is defined by $nsProduct) using specified
"subpath" inside that nodeset.

In some sense, the problem is similar to what XForms are trying to
achieve: provide XML description of a data model, XML description of a
view of that data model, and bindings between view elements and data
model elements. In my case, "bindings" are supposed to be very
simplistic - just plain relative XPath expressions...

If anyone knows the trick that could let me do what I want (or
something really close to what I want) using XSLT 2.0 and XPath 2.0,
please advise. In fact, any comments would be appreciated.

Thank you,
Yarik.
 
J

Joe Kesselman

Dynamic interpretation of XPaths from variables is not supported in XSLT
.... but may be possible in some specific processors via extension
functions. See
http://www.exslt.org/dyn/

Instead, I'd consider writing your "template file" so it can be executed
directly as a stylesheet. One approach to doing so is shown in the XSLT
spec itself:
http://www.w3.org/TR/xslt#result-element-stylesheet

Or, if you really want to separate the binding stage from the styling
stage, you could consider two-pass transformation -- one pass to extract
the data and put it in standard form, and a second to get it from that
into your presentation form.
 
Y

Yarik

Joe, thank you much for quick and thorough response!

Joe said:
Dynamic interpretation of XPaths from variables is not supported
in XSLT ... but may be possible in some specific processors
via extension functions.

I'm using Saxon, and it does not seem to support dyn: extensions. But,
even if it did, using EXSLT would be my last resort. (Or next to last.
:))
Instead, I'd consider writing your "template file" so it can be executed
directly as a stylesheet. One approach to doing so is shown in the XSLT
spec itself:
http://www.w3.org/TR/xslt#result-element-stylesheet

This probably would be my first choice should I lose my battle for "a
la XForms" approach...
Or, if you really want to separate the binding stage from the styling
stage, you could consider two-pass transformation...

I guess, this would be my second choice...

In the meantime, I did some quick-and-dirty experiments and... it turns
out that "vanilla" XSLT 2.0 is not as weak as I thought. If anyone is
interested, my experiment yielded the following interesting, yet very
simple, custom function (i.e. function template):

<xs:function name="custom:path" xmlns:custom="custom">
<xs:param name="nsNode"/>
<xs:param name="nsRoot"/>

<xs:if test="not($nsNode is $nsRoot)">
<xs:value-of
select="concat(custom:path($nsNode/parent::node(),$nsRoot), '/',
name($nsNode))"/>
</xs:if>

</xs:function>

It takes two nodes and returns a string that represents a path from
$nsRoot to $nsNode (assuming that $nsNode is a descendant of $nsRoot).

Now, taking my initial example, I can use this nice function like this:

<xs:template match="ProductProperty">
<!-- The parameter contains a nodeset from Products.xml -->
<!-- Or there could be a variable with the same contents... -->

<xs:param name="nsProduct" tunnel="yes"/>
<!-- And below is where the fun begins... -->
<xs:apply-templates select="$nsProduct/*[custom:path(.,
$nsProduct) = current()/@rxpath]"/>
</xs:template>

Maybe this is not the most elegant way to do it, but it appears to be
working just fine. Moreover, I suspect that, with some additional
effort, this function could be enhanced to handle more intricate cases.
But... at 4 o'clock in the morning I would not bet my head on this...
:)

The more I use XSLT 2.0, the more I like it (and the more XSLT 1.0
feels like a stone-age tool :).
 

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,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top