Re: [xsl] Re: How to handle dynamic XPath

Subject: Re: [xsl] Re: How to handle dynamic XPath
From: Dimitre Novatchev <dnovatchev@xxxxxxxxx>
Date: Mon, 13 Apr 2009 06:50:44 -0700
Finished my reply too-soon.

Another major use of an XPath 2.0 parser could be in a refactoring
tool, and also adding to Mukuls Lint the so needed XPath expression
analysis.


--
Cheers,
Dimitre Novatchev
---------------------------------------
Truly great madness cannot be achieved without significant intelligence.
---------------------------------------
To invent, you need a good imagination and a pile of junk
-------------------------------------
Never fight an inanimate object
-------------------------------------
You've achieved success in your field when you don't know whether what
you're doing is work or play



On Mon, Apr 13, 2009 at 6:46 AM, Dimitre Novatchev <dnovatchev@xxxxxxxxx>
wrote:
>> Mike's solution works, although I would prefer a solution without
extension
>> functions.
>
> One could use the FXSL LR-Parsing framework to create an XPath parser.
>
> This is what I did more than an year ago (an XPath 2.0) parser, and
> already forgot about it -- could find no use for it until recently,
> when Florent asked for the parser with the intent to use it for
> hi-lighting XPath expressions.
>
> Another use could be to convert a complicated XPath expression into
> corresponding equivalent XSLT instructions -- for people who prefer
> the XSLT-oriented syntax over the XPath-oriented one.
>
> Or, if we could use the XPath 2.0 grammer to *synthesize* XPath
> expressions, then one could create an app. to convert XSLT to an XPath
> expression. Anyone heard of a usable instance generator, as opposed to
> a parser, using a grmmar definition?
>
>
>
> --
> Cheers,
> Dimitre Novatchev
> ---------------------------------------
> Truly great madness cannot be achieved without significant intelligence.
> ---------------------------------------
> To invent, you need a good imagination and a pile of junk
> -------------------------------------
> Never fight an inanimate object
> -------------------------------------
> You've achieved success in your field when you don't know whether what
> you're doing is work or play
>
>
>
> On Sun, Apr 12, 2009 at 11:28 PM, B <fred@xxxxxxxxxxxxx> wrote:
>> This list is incredible!
>> I was struggling all Easter sunday with a problem, posted it, went to
sleep
>> and overnight three experts came with solutions. Thank you, Ken, Michael
and
>> Florent!
>>
>> Mike's solution works, although I would prefer a solution without
extension
>> functions.
>> Keeping a key table as Ken suggests is a bit difficult, as beforehand
>> (design time) I do not know the structure of the documents. The XSL takes
>> any XML schema that obeys certain rules of well-formedness. Creating the
key
>> table at run time could be a solution though.
>> Florents suggestion of a meta-stylesheet is worth wile investigating, but
>> that would complicate the application even more.
>>
>> Hope I didnt spoil your Easter too much.
>>
>> Fred
>>
>>
>>
>>
>>> ------------------------------
>>>
>>> Date: Sun, 12 Apr 2009 21:07:29 +0200
>>> To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx
>>> From: fred@xxxxxxxxxxxxx
>>> Subject: How to handle dynamic XPath
>>> Message-ID: <20090412210729.uv66b6hsw0sgksw0@xxxxxxxxxxxxxxxxxxxxx>
>>>
>>> Hi,
>>>
>>> I am using XSLT to walk through an XML Schema to construct an Xforms =20
>>> (output) instance.
>>> In parallel I am scanning an (input) instance (of a document defined =20
>>> by the schema) to
>>> include default values in the form.
>>>
>>> Scanning of the input instance is done by means of a variable that =20
>>> keeps track of the
>>> path. The variable is updated (renewed) each time a template is =20
>>> (recursively) called with
>>> the variable as parameter:
>>>
>>> <xsl:with-param name=3D"instance.path"
>>> select=3D"concat($instance.path,'/',$element.prefix,':',@name)" />
>>>
>>> The template calls another template that writes the output instance. =20
>>> In that template I
>>> try to look up the default value in the input instance (an external
>>> document=
>>> ).
>>>
>>> BUT:
>>>
>>> <xsl:value-of
select=3D"document('file:/C:/dir/order.xml')/$instance.path"
>>> /=
>>>>
>>> writes a path string to the output instance instead of the value of =20
>>> the element, such as:
>>> /rsm:PurchaseOrder/rsm:Identifier, so does copy-of select.
>>>
>>> <xsl:value-of
>>> select=3D"document('file:/C:/dir/order.xml')//rsm:DeliveryDate=
>>> " />
>>> writes correctly the content of the delivery date, but
>>> <xsl:value-of select=3D"document('file:/C:/dir/order.xml')//$deldate" />
>>> (where $deldate contains the string "rsm:DeliveryDate") writes nothing.
>>>
>>> Concluding the problem is node-format vs text format, I tried the =20
>>> saxon evaluate
>>> function, but:
>>> <xsl:copy-of =20
>>>
>>>
select=3D"saxon:evaluate(document('file:/C:/dir/order.xml')/$instance.path)"=
>>> B =20
>>> />
>>> writes nothing, neither does value-of.
>>>
>>> I am desperate. Does anyone have a clue how to solve this?
>>>
>>> Thanks in advance,
>>>
>>> Fred van Blommestein
>>>
>>> ------------------------------
>>>
>>> Date: Sun, 12 Apr 2009 15:34:14 -0400
>>> To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx
>>> From: "G. Ken Holman" <gkholman@xxxxxxxxxxxxxxxxxxxx>
>>> Subject: Re: [xsl] How to handle dynamic XPath
>>> Message-Id: <7.0.1.0.2.20090412152437.02699d30@xxxxxxxxxxxxxxxxxxxxxx>
>>>
>>> At 2009-04-12 21:07 +0200, fred@xxxxxxxxxxxxx wrote:
>>>>
>>>> I am using XSLT to walk through an XML Schema to construct an Xforms
>>>> (output) instance.
>>>> In parallel I am scanning an (input) instance (of a document defined
>>>> by the schema) to
>>>> include default values in the form.
>>>
>>> I'm assuming the structures of the two documents are identical.
>>>
>>>> Scanning of the input instance is done by means of a variable that
>>>> keeps track of the
>>>> path. The variable is updated (renewed) each time a template is
>>>> (recursively) called with
>>>> the variable as parameter:
>>>>
>>>> <xsl:with-param name="instance.path"
>>>> select="concat($instance.path,'/',$element.prefix,':',@name)" />
>>>>
>>>> The template calls another template that writes the output instance.
>>>> In that template I
>>>> try to look up the default value in the input instance (an external
>>>> B document).
>>>>
>>>> BUT:
>>>>
>>>> <xsl:value-of select="document('file:/C:/dir/order.xml')/$instance.path"
>>>> />
>>>> writes a path string to the output instance instead of the value of
>>>> the element, such as:
>>>> /rsm:PurchaseOrder/rsm:Identifier, so does copy-of select.
>>>
>>> Correct, because there is no such thing as "eval()" in XPath. B The
>>> final step of your XPath address is the value of a variable, so you
>>> are getting the value of the variable as you should.
>>>
>>> And who is to say that your prefixes in your input document match the
>>> prefixes in your default value document?
>>>
>>>> <xsl:value-of
>>>> B select="document('file:/C:/dir/order.xml')//rsm:DeliveryDate" />
>>>> writes correctly the content of the delivery date, but
>>>> <xsl:value-of select="document('file:/C:/dir/order.xml')//$deldate" />
>>>> (where $deldate contains the string "rsm:DeliveryDate") writes nothing.
>>>
>>> I would expect you to see the string "rsm:DeliveryDate", not nothing.
>>>
>>>> Concluding the problem is node-format vs text format, I tried the
>>>> saxon evaluate
>>>> function, but:
>>>> <xsl:copy-of
>>>>
>>>>
select="saxon:evaluate(document('file:/C:/dir/order.xml')/$instance.path)"
>>>> />
>>>> writes nothing, neither does value-of.
>>>>
>>>> I am desperate. Does anyone have a clue how to solve this?
>>>
>>> You could use a key table, where the lookup value for each element is
>>> that element's fully-qualified XPath address. B And you could use the
>>> "{namespace-uri}local-name" syntax for each element in the path in
>>> order to be independent of namespace prefixes.
>>>
>>> Then you use:
>>>
>>> B <xsl:value-of select="document('file:/C:/dir/order.xml')/
>>> B  B  B  B  B  B  B  B  B  B  B  B key('myTable',$instance.path))"/>
>>>
>>> and it will return the value of the element.
>>>
>>> You don't give any sample data to test with, and you don't talk about
>>> sibling elements of the same name, but I think this approach would
>>> work for you. B An example is below where each element of fred.xml is
>>> replaced with the corresponding value from fred-default.xml.
>>>
>>> I hope this helps.
>>>
>>> . . . . . . . . . . . . . Ken
>>>
>>> T:\ftemp>type fred.xml
>>> <a xmlns="urn:x-fred">
>>> B  <b>B1</b>
>>> B  <c>C1</c>
>>> B  <b>B2</b>
>>> B  <d>
>>> B  B  <e>E1</e>
>>> B  </d>
>>> </a>
>>> T:\ftemp>type fred-default.xml
>>> <f:a xmlns:f="urn:x-fred">
>>> B  <f:b>def-B1</f:b>
>>> B  <f:c>def-C1</f:c>
>>> B  <f:b>def-B2</f:b>
>>> B  <f:d>
>>> B  B  <f:e>def-E1</f:e>
>>> B  </f:d>
>>> </f:a>
>>> T:\ftemp>call xslt2 fred.xml fred.xsl
>>> <?xml version="1.0" encoding="UTF-8"?><a xmlns="urn:x-fred">
>>> B  <b>def-B1</b>
>>> B  <c>def-C1</c>
>>> B  <b>def-B2</b>
>>> B  <d>
>>> B  B  <e>def-E1</e>
>>> B  </d>
>>> </a>
>>> T:\ftemp>type fred.xsl
>>> <?xml version="1.0" encoding="US-ASCII"?>
>>> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
>>> B  B  B  B  B  B  B  B  xmlns:f="urn:x-fred"
>>> B  B  B  B  B  B  B  B  version="2.0">
>>>
>>> <xsl:key name="paths" match="*" use="f:make-path(.)"/>
>>>
>>> <!--walk the element tree-->
>>> <xsl:template match="*">
>>> B  <xsl:param name="path"/>
>>> B  <!--determine the path to this element-->
>>> B  <xsl:variable name="this-path"
>>> B  B  B  B  B  B  B  B  select="concat($path,f:make-step(.))"/>
>>> B  <!--preserve the element structure-->
>>> B  <xsl:copy>
>>> B  B  <!--preserve any attributes-->
>>> B  B  <xsl:apply-templates select="@*"/>
>>> B  B  <!--replace the value when there are no element children-->
>>> B  B  <xsl:choose>
>>> B  B  B  <xsl:when test="not(*)">
>>> B  B  B  B  <!--at a leaf element-->
>>> B  B  B  B  <xsl:value-of select="document('fred-default.xml')/
>>> B  B  B  B  B  B  B  B  B  B  B  B  B  B  B  key('paths',$this-path)"/>
>>> B  B  B  </xsl:when>
>>> B  B  B  <xsl:otherwise>
>>> B  B  B  B  <!--at a branch element, keep going-->
>>> B  B  B  B  <xsl:apply-templates>
>>> B  B  B  B  B  <xsl:with-param name="path" select="$this-path"/>
>>> B  B  B  B  </xsl:apply-templates>
>>> B  B  B  </xsl:otherwise>
>>> B  B  </xsl:choose>
>>> B  </xsl:copy>
>>> </xsl:template>
>>>
>>> <!--identity for all other nodes-->
>>> <xsl:template match="@*|comment()|processing-instruction()">
>>> B  <xsl:copy/>
>>> </xsl:template>
>>>
>>> <!--create a lookup key-->
>>> <xsl:function name="f:make-path">
>>> B  <xsl:param name="element"/>
>>> B  <xsl:sequence select="string-join( for $e in
>>> $element/ancestor-or-self::*
>>> B  B  B  B  B  B  B  B  B  B  B  B  B  B  B  B  B  B  B return
f:make-step( $e ), '' )"/>
>>> </xsl:function>
>>>
>>> <xsl:function name="f:make-step">
>>> B  <xsl:param name="element"/>
>>> B  <xsl:for-each select="$element">
>>> B  B  <xsl:sequence
select="concat('/{',namespace-uri(.),'}',local-name(.),
>>>
>>> B '[',count(preceding-sibling::*[node-name(.)=node-name(current())])+1,
>>> B  B  B  B ']')"/>
>>> B  </xsl:for-each>
>>> </xsl:function>
>>>
>>> </xsl:stylesheet>
>>>
>>> T:\ftemp>rem Done!
>>>
>>> --
>>> XSLT/XSL-FO/XQuery training in Los Angeles (New dates!) 2009-06-08
>>> Training tools: Comprehensive interactive XSLT/XPath 1.0/2.0 video
>>> Video lesson: B  B http://www.youtube.com/watch?v=PrNjJCh7Ppg&fmt=18
>>> Video overview: B http://www.youtube.com/watch?v=VTiodiij6gE&fmt=18
>>> G. Ken Holman B  B  B  B  B  B  B  B 
mailto:gkholman@xxxxxxxxxxxxxxxxxxxx
>>> Crane Softwrights Ltd. B  B  B  B  B http://www.CraneSoftwrights.com/s/
>>> Male Cancer Awareness Nov'07 B http://www.CraneSoftwrights.com/s/bc
>>> Legal business disclaimers: B http://www.CraneSoftwrights.com/legal
>>>
>>> ------------------------------
>>>
>>> Date: Sun, 12 Apr 2009 23:35:31 +0100
>>> To: <xsl-list@xxxxxxxxxxxxxxxxxxxxxx>
>>> From: "Michael Kay" <mike@xxxxxxxxxxxx>
>>> Subject: RE: [xsl] How to handle dynamic XPath
>>> Message-ID: <4FA95E4459EE4A4B85EEF35A0C54B439@Sealion>
>>>
>>>> Concluding the problem is node-format vs text format, I tried
>>>> the saxon evaluate function, but:
>>>> <xsl:copy-of
>>>> select="saxon:evaluate(document('file:/C:/dir/order.xml')/$ins
>>>> tance.path)"
>>>> />
>>>> writes nothing, neither does value-of.
>>>
>>> You're on the right lines here, but the syntax would be
>>>
>>>
select="document('file:/C:/dir/order.xml')/saxon:evaluate($instance.path)"
>>>
>>> I strongly suspect that there are much better solutions to this problem
>>> that
>>> don't involve dynamic evaluation. However, it's Easter Sunday.
>>>
>>> Michael Kay
>>> http://www.saxonica.com/
>>>
>>> ------------------------------
>>>
>>> Date: Sun, 12 Apr 2009 19:52:19 +0000 (GMT)
>>> To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx
>>> From: Florent Georges <lists@xxxxxxxxxxxx>
>>> Subject: Re : [xsl] How to handle dynamic XPath
>>> Message-ID: <816269.32982.qm@xxxxxxxxxxxxxxxxxxxxxxxxxxx>
>>>
>>> fred@xxxxxxxxxxxxx wrote:
>>>
>>> B Hi,
>>>
>>>> <xsl:with-param name=3D"instance.path"
>>>> select=3D"concat($instance.path,'/',$element.prefix,':',@name)"
>>>> />
>>>
>>> B You cannot evaluate an XPath expression supplied as a string in plain
>>> XSL=
>>> T. B You can use extensions though for that purpose (of your own or
>>> existing=
>>> B one.) B I haven't looked deeply into your problem, but I'd say I'd
rather
>>> u=
>>> ser either:
>>>
>>> B 1/ maintaining a pointer in the instance and selecting its correct
>>> child(=
>>> ren) using local-name() and namespace-uri() for each step;
>>>
>>> B 2/ or using a meta-stylesheet: your stylesheet generates another XSLT
>>> sty=
>>> lesheet that contains the XPath expressions (you "compile" the schema to
>>> th=
>>> e equivalent stylesheet) and you apply this stylesheet to the instance in
>>> a=
>>> B second execution.
>>>
>>> B Regards,
>>>
>>> --=20
>>> Florent Georges
>>> http://www.fgeorges.org/
>>>
>>> =0A=0A=0A
>>>
>>> ------------------------------
>>>
>>> End of xsl-list Digest
>>> ***********************************

Current Thread