Thread: [Xsltforms-support] applying a stylesheet in an instance to data in an instance
Brought to you by:
alain-couthures
From: C. M. Sperberg-M. <cm...@bl...> - 2012-07-12 21:16:01
|
In a form I'm working on, I'd like to apply a stylesheet located in a document instance (instance('filter') to an input document located in another instance (instance('userdoc')). It doesn't seem to be working, and I wonder if anyone here can see what I'm doing wrong. I've studied the code for the existing transform() function, which accepts a node set and a URI and then calls XsltForms_browser.transformText with that same node set, that same URI, and a flag to signal that the second argument is a URI and not a literal string containing an angle-bracket encoding of a stylesheet. If I understand the code for the underlying transformText function correctly (not guaranteed!), it can accept either a URI or a stylesheet in string form in its second argument, and its third argument tells the system which case applies. - If the third argument is true, the second argument is passed to a parser and a DOM object is returned. - If the third argument is false, the second argument is used to load an external document, and the resulting DOM is returned. (At least, I assume the results are DOM objects.) Other calls to transformText() seem to use strings successfully in the second argument. So I wrote an extension function that looks like this: function bmt_applyxslt_ns_s(nsXML, sXSLT) { if (arguments.length !== 2) { throw XsltForms_xpathFunctionExceptions.transformInvalidArgumentsNumber; } return nsXML.length === 0? "" : XsltForms_browser.transformText(XsltForms_browser.saveXML(nsXML[0]), sXSLT, true); } This is almost verbatim the same as the source code for transform(), except for the argument names and 'true' instead of 'false' in the third argument. (I notice now that I ought to change the name of the exception, perhaps, too.) I register the function with XSLTforms in the usual way. <xf:setvalue ref="instance('userout')/self::data" value="bmt:s_applyxslt_ns_s(instance('userdoc'), serialize(instance('filter')))"/> The form loads as expected and the function seems to be called without incident and without error messages (and alert messages confirm that the arguments look pretty much as I expect them to look). But the result is consistently not the output I expect from the stylesheet, but a document which reads in its entirety: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml"> <head><title></title></head> <body> <pre></pre> </body> </html> Anyone have a clue? Anyone have a better (or just different) way to go about this? Thanks! Michael Sperberg-McQueen -- **************************************************************** * C. M. Sperberg-McQueen, Black Mesa Technologies LLC * http://www.blackmesatech.com * http://cmsmcq.com/mib * http://balisage.net **************************************************************** |
From: C. M. Sperberg-M. <cm...@bl...> - 2012-07-14 02:32:15
|
[Those not interested in interim or partial results relating to the problem of running XSLT in a form can skip this message and wait for a post mortem analysis later on, once the problem is finally resolved.] On Jul 12, 2012, at 3:15 PM, C. M. Sperberg-McQueen wrote: > In a form I'm working on, I'd like to apply a stylesheet located in a > document instance (instance('filter') to an input document located in > another instance (instance('userdoc')). > > It doesn't seem to be working, and I wonder if anyone here can see > what I'm doing wrong. > > ... > > The form loads as expected and the function seems to be called > without incident and without error messages (and alert messages > confirm that the arguments look pretty much as I expect them to look). > But the result is consistently not the output I expect from the stylesheet, > but a document which reads in its entirety: > > <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml"> > <head><title></title></head> > <body> > <pre></pre> > </body> > </html> Further data: This is what the output looks like in Safari and Chrome, but in Firefox I get instead something like: <?xml version="1.0" encoding="UTF-8"?> <transformiix:result xmlns:transformiix="http://www.mozilla.org/TransforMiix"></transformiix:result> More generally, all the browsers I've tested appear to force the output of any XSLT transformation to XML (or HTML); if the stylesheet uses xsl:output method="text", the output gets wrapped in something -- in the case of Safari and Chrome, it gets wrapped in an HTML document with a pre element, while in Firefox it gets wrapped in a transformiix:result element. In Opera, the current version of my form doesn't work at all yet, but a test case with some simple transforms shows that in Opera, the output of a text-method XSLT stylesheet is wrapped in a 'result' element (and preceded by an XML declaration). So the result I was seeing with Safari (shown above) seems to indicate that the transformation I was trying to run was producing no output. > > Anyone have a clue? Anyone have a better (or just different) way to go about > this? This continues to be a live question; if I get this working, I'll try to document what I learned, in the hopes that it can help others. Michael -- **************************************************************** * C. M. Sperberg-McQueen, Black Mesa Technologies LLC * http://www.blackmesatech.com * http://cmsmcq.com/mib * http://balisage.net **************************************************************** |
From: C. M. Sperberg-M. <cm...@bl...> - 2012-07-15 20:14:36
|
On Jul 12, 2012, at 3:15 PM, C. M. Sperberg-McQueen wrote: > In a form I'm working on, I'd like to apply a stylesheet located in a > document instance (instance('filter') to an input document located in > another instance (instance('userdoc')). For others interested in this topic, I should record that this is in fact possible and a simple extension function does the job. A number of test cases that demonstrate the use of transform() and an extension function for instance-based XSLT transforms can be found at http://blackmesatech.com/2012/07/testcase/transform/index.xml Michael Sperberg-McQueen > ... > So I wrote an extension function that looks like this: > > function bmt_applyxslt_ns_s(nsXML, sXSLT) { > if (arguments.length !== 2) { > throw XsltForms_xpathFunctionExceptions.transformInvalidArgumentsNumber; > } > return nsXML.length === 0? "" : XsltForms_browser.transformText(XsltForms_browser.saveXML(nsXML[0]), sXSLT, true); > } > > This is almost verbatim the same as the source code for transform(), > except for the argument names and 'true' instead of 'false' in the > third argument. This code appears to work correctly, given a good stylesheet. > I register the function with XSLTforms in the usual way. > > <xf:setvalue > ref="instance('userout')/self::data" > value="bmt:s_applyxslt_ns_s(instance('userdoc'), serialize(instance('filter')))"/> > > The form loads as expected and the function seems to be called > without incident and without error messages (and alert messages > confirm that the arguments look pretty much as I expect them to look). > But the result is consistently not the output I expect from the stylesheet, > but a document which reads in its entirety: > > <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml"> > <head><title></title></head> > <body> > <pre></pre> > </body> > </html> > > Anyone have a clue? Anyone have a better (or just different) way to go about > this? This appears to be Safari's way of signaling an error of some kind in the execution of the XSLT transform. -- **************************************************************** * C. M. Sperberg-McQueen, Black Mesa Technologies LLC * http://www.blackmesatech.com * http://cmsmcq.com/mib * http://balisage.net **************************************************************** |
From: Alain C. <ala...@ag...> - 2012-07-16 08:10:16
|
The "transform()" function is not yet fully specified and it would be easy to temporarily add a third parameter to it to switch from URI to text. As you mention it in your test case, another situation is whether the node for the stylesheet is a leaf or a sub-tree to be serialized and a fourth parameter could indicate this. Currently, XSLTForms only supports a text result for the transformation and it can be used within a "setvalue" action. A sort of "loadXML" action could easily be added so the result will be loaded as a sub-tree. What do you think? Thanks! -Alain Le 15/07/2012 22:14, C. M. Sperberg-McQueen a écrit : > On Jul 12, 2012, at 3:15 PM, C. M. Sperberg-McQueen wrote: > >> In a form I'm working on, I'd like to apply a stylesheet located in a >> document instance (instance('filter') to an input document located in >> another instance (instance('userdoc')). > For others interested in this topic, I should record that this is in fact > possible and a simple extension function does the job. > > A number of test cases that demonstrate the use of transform() and > an extension function for instance-based XSLT transforms can be > found at > > http://blackmesatech.com/2012/07/testcase/transform/index.xml > > Michael Sperberg-McQueen > > >> ... >> So I wrote an extension function that looks like this: >> >> function bmt_applyxslt_ns_s(nsXML, sXSLT) { >> if (arguments.length !== 2) { >> throw XsltForms_xpathFunctionExceptions.transformInvalidArgumentsNumber; >> } >> return nsXML.length === 0? "" : XsltForms_browser.transformText(XsltForms_browser.saveXML(nsXML[0]), sXSLT, true); >> } >> >> This is almost verbatim the same as the source code for transform(), >> except for the argument names and 'true' instead of 'false' in the >> third argument. > This code appears to work correctly, given a good stylesheet. > >> I register the function with XSLTforms in the usual way. >> >> <xf:setvalue >> ref="instance('userout')/self::data" >> value="bmt:s_applyxslt_ns_s(instance('userdoc'), serialize(instance('filter')))"/> >> >> The form loads as expected and the function seems to be called >> without incident and without error messages (and alert messages >> confirm that the arguments look pretty much as I expect them to look). >> But the result is consistently not the output I expect from the stylesheet, >> but a document which reads in its entirety: >> >> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml"> >> <head><title></title></head> >> <body> >> <pre></pre> >> </body> >> </html> >> >> Anyone have a clue? Anyone have a better (or just different) way to go about >> this? > This appears to be Safari's way of signaling an error of some kind in the > execution of the XSLT transform. > > |
From: C. M. Sperberg-M. <cm...@bl...> - 2012-07-16 14:33:04
|
On Jul 16, 2012, at 2:09 AM, Alain Couthures wrote: > The "transform()" function is not yet fully specified and it would be > easy to temporarily add a third parameter to it to switch from URI to text. > > As you mention it in your test case, another situation is whether the > node for the stylesheet is a leaf or a sub-tree to be serialized and a > fourth parameter could indicate this. > > Currently, XSLTForms only supports a text result for the transformation > and it can be used within a "setvalue" action. A sort of "loadXML" > action could easily be added so the result will be loaded as a sub-tree. > > What do you think? For what it's worth, I think it would be useful to have a standard way to load the result of a transform into an instance. In case an example helps, the form I was working on this past week may serve as a simple use case. It involves two XSLT processes: first, the user loads an XML document and a static stylesheet (the 'factory') runs over the user's document and produces a second stylesheet (the 'filter'), and then the user can configure the filter by changing various default behaviors. Whey they are done, they can save the filter to disk or apply the filter to the user's document. Since the point of the filter is to produce untagged text for the user to feed into text analysis or statistical software that does not understand XML (it's sad to think that people still use such software, but I'm told they do), there is no particular need to load the XML structure of the second transformation result into an instance. (Initially I used the 'text' output method in the transform, but all the browsers I tested appear to insist on producing either XML or HTML output from a transform, so I switched to the XML output method and wrapped the text result in an 'output' element.) But the result of the first transformation MUST be loaded into an instance for the form to serve its purpose; the point of the form is to illustrate, for a panel discussion at a conference next week, (a) that it's not enough just to 'strip the tags out' of an XML document and (b) that it's possible to provide an interface to allow an intelligent selection from the encoded document. So the user must be able to edit the filter, which means the filter must be an XML instance. In this case, I handled the lack of 'loadXML' action by using a simple trick: I submit the text result of the initial transformation to a script on the server, which does nothing but bounce it back to the client as text/xml. The submission is defined this way: <xf:submission id="load-userxslt" ref="instance('filter')/text()" method="post" resource="../../../lib/reflect-as-xml.sh" replace="instance" instance="filter" /> The 'filter' instance initially contains the text-node output from the transformation; this submission sends that output (without the outermost wrapper element, which is not needed) to the server, where 'reflect-as-xml.sh' does nothing but send it back with an HTTP header. Reduced to its essentials, reflect-as-xml.sh looks like this: #!/bin/sh echo "Content-Type: text/xml" echo cat It would be a nicer solution, however, if no server round-trip were required here. (I thought about trying to figure out how to do that in Javascript with an extension function or extension action, but I decided to go with this quicker solution, for purposes of the demo.) In case it's of interest to readers of this list, the form in question is now visible at http://blackmesatech.com/2012/07/dh/sdb.xml The form is intended as a toy to illustrate some general principles, and not as a tool for really serious work. (So in particular, don't give it a very large document to work with. It works fine on a TEI-encoded sonnet, and it takes several minutes [and counting] to handle one of Shakespeare's five-act plays.) -- **************************************************************** * C. M. Sperberg-McQueen, Black Mesa Technologies LLC * http://www.blackmesatech.com * http://cmsmcq.com/mib * http://balisage.net **************************************************************** |
From: Alain C. <ala...@ag...> - 2012-07-17 21:18:19
|
Rev. 550 includes a new experimental action named "setnode" with @inner or @outer attributes to perform a "loadXML" for the corresponding node. Here is a test case: <html xmlns="http://www.w3.org/1999/xhtml" xmlns:xf="http://www.w3.org/2002/xforms" xmlns:ev="http://www.w3.org/2001/xml-events"> <head> <title>Inner & Outer Set Node</title> <xf:model> <xf:instance> <data xmlns=""> <item>a</item> </data> </xf:instance> </xf:model> </head> <body> <h1>Inner & Outer Set Node</h1> <xf:output value="item"/> <xf:trigger> <xf:label>Inner</xf:label> <xf:setnode ref="." ev:event="DOMActivate" inner="'<item>b</item>'"/> </xf:trigger> <xf:trigger> <xf:label>Outer</xf:label> <xf:setnode ref="item" ev:event="DOMActivate" outer="'<item>c</item>'"/> </xf:trigger> </body> </html> Le 16/07/2012 16:32, C. M. Sperberg-McQueen a écrit : > For what it's worth, I think it would be useful to have a standard way > to load the result of a transform into an instance. > > In case an example helps, the form I was working on this past week > may serve as a simple use case. It involves two XSLT processes: > first, the user loads an XML document and a static stylesheet (the > 'factory') runs over the user's document and produces a second > stylesheet (the 'filter'), and then the user can configure the filter by > changing various default behaviors. Whey they are done, they can > save the filter to disk or apply the filter to the user's document. > > Since the point of the filter is to produce untagged text for the user > to feed into text analysis or statistical software that does not understand > XML (it's sad to think that people still use such software, but I'm told > they do), there is no particular need to load the XML structure of the > second transformation result into an instance. (Initially I used the > 'text' output method in the transform, but all the browsers I tested > appear to insist on producing either XML or HTML output from a > transform, so I switched to the XML output method and wrapped > the text result in an 'output' element.) > > But the result of the first transformation MUST be loaded into an > instance for the form to serve its purpose; the point of the form is > to illustrate, for a panel discussion at a conference next week, > (a) that it's not enough just to 'strip the tags out' of an XML document > and (b) that it's possible to provide an interface to allow an > intelligent selection from the encoded document. So the user > must be able to edit the filter, which means the filter must be an > XML instance. > > In this case, I handled the lack of 'loadXML' action by using a simple > trick: I submit the text result of the initial transformation to a script > on the server, which does nothing but bounce it back to the client > as text/xml. The submission is defined this way: > > <xf:submission id="load-userxslt" > ref="instance('filter')/text()" > method="post" > resource="../../../lib/reflect-as-xml.sh" > replace="instance" > instance="filter" > /> > > The 'filter' instance initially contains the text-node output from the > transformation; this submission sends that output (without the > outermost wrapper element, which is not needed) to the server, > where 'reflect-as-xml.sh' does nothing but send it back with an HTTP > header. Reduced to its essentials, reflect-as-xml.sh looks like this: > > #!/bin/sh > echo "Content-Type: text/xml" > echo > cat > > It would be a nicer solution, however, if no server round-trip were > required here. (I thought about trying to figure out how to do that > in Javascript with an extension function or extension action, but I > decided to go with this quicker solution, for purposes of the demo.) > > In case it's of interest to readers of this list, the form in question > is now visible at > > http://blackmesatech.com/2012/07/dh/sdb.xml > > The form is intended as a toy to illustrate some general principles, > and not as a tool for really serious work. (So in particular, don't > give it a very large document to work with. It works fine on a > TEI-encoded sonnet, and it takes several minutes [and counting] to > handle one of Shakespeare's five-act plays.) > > |
From: C. M. Sperberg-M. <cm...@bl...> - 2012-07-17 22:04:04
|
On Jul 17, 2012, at 3:17 PM, Alain Couthures wrote: > Rev. 550 includes a new experimental action named "setnode" with @inner or @outer attributes to perform a "loadXML" for the corresponding node. That looks great! Thank you very much. > > Here is a test case: > <html xmlns="http://www.w3.org/1999/xhtml" xmlns:xf="http://www.w3.org/2002/xforms" xmlns:ev="http://www.w3.org/2001/xml-events"> > <head> > <title>Inner & Outer Set Node</title> > <xf:model> > <xf:instance> > <data xmlns=""> > <item>a</item> > </data> > </xf:instance> > </xf:model> > </head> > <body> > <h1>Inner & Outer Set Node</h1> > <xf:output value="item"/> > <xf:trigger> > <xf:label>Inner</xf:label> > <xf:setnode ref="." ev:event="DOMActivate" inner="'<item>b</item>'"/> > </xf:trigger> > <xf:trigger> > <xf:label>Outer</xf:label> > <xf:setnode ref="item" ev:event="DOMActivate" outer="'<item>c</item>'"/> > </xf:trigger> > </body> > </html> This works fine. To try to make sure I understood the new action, however, I tried the following variation, which works as expected with one exception: <html xmlns="http://www.w3.org/1999/xhtml" xmlns:xf="http://www.w3.org/2002/xforms" xmlns:ev="http://www.w3.org/2001/xml-events"> <head> <title>Inner & Outer Set Node</title> <xf:model> <xf:instance> <data xmlns=""> <item>a</item> </data> </xf:instance> </xf:model> </head> <body style="margin: 0.5em;"> <h1>Inner & Outer Set Node</h1> <p>The instance currently looks like this:</p> <pre> <xf:output value="serialize(.)" mediatype="text/plain"> </xf:output> </pre> <hr/> <p>These buttons apply the <code>setnode</code> action to the instance root (<code>.</code>):</p> <xf:trigger> <xf:label>Set Inner to '<item>b</item>'</xf:label> <xf:setnode ref="." ev:event="DOMActivate" inner="'<item>b</item>'"/> </xf:trigger> <xf:trigger> <xf:label>Set Outer to '<item>c</item>'</xf:label> <xf:setnode ref="." ev:event="DOMActivate" outer="'<item>c</item>'"/> </xf:trigger> <hr/> <p>These buttons apply the <code>setnode</code> action to the child of the instance root (<code>./item</code>):</p> <xf:trigger> <xf:label>Set Inner to '<item>d</item>'</xf:label> <xf:setnode ref="./item" ev:event="DOMActivate" inner="'<item>d</item>'"/> </xf:trigger> <xf:trigger> <xf:label>Set Outer to '<item>e</item>'</xf:label> <xf:setnode ref="./item" ev:event="DOMActivate" outer="'<item>e</item>'"/> </xf:trigger> </body> </html> The second button of the first pair attempts to supply an outer value for the outermost element of the instance (.) -- essentially, that is, it attempts to replace the instance entirely. The other buttons work as I expected, but this one doesn't seem to have the desired effect (and after I click it, the others stop working). My idea is that setnode ref="instance('x')" outer="..."/> should have essentially the same effect as <xf:submit target="bounce-xml-string-back-as-xml" replace="instance" instance="x" ref="y"/> when y is an XPath expression pointing to a node set whose serialization is the same string as given in the 'outer' attribute. If that's not possible from an implementation point of view, I can live with it. But I predict I won't be the last user who hopes it will work that way :) In any case, thank you again, this is great. -- **************************************************************** * C. M. Sperberg-McQueen, Black Mesa Technologies LLC * http://www.blackmesatech.com * http://cmsmcq.com/mib * http://balisage.net **************************************************************** |
From: Alain C. <ala...@ag...> - 2012-07-18 20:03:17
|
Le 18/07/2012 00:03, C. M. Sperberg-McQueen a écrit : > This works fine. To try to make sure I understood the new action, > however, I tried the following variation, which works as expected > with one exception: > > <html xmlns="http://www.w3.org/1999/xhtml" xmlns:xf="http://www.w3.org/2002/xforms" xmlns:ev="http://www.w3.org/2001/xml-events"> > <head> > <title>Inner & Outer Set Node</title> > <xf:model> > <xf:instance> > <data xmlns=""> > <item>a</item> > </data> > </xf:instance> > </xf:model> > </head> > <body style="margin: 0.5em;"> > <h1>Inner & Outer Set Node</h1> > <p>The instance currently looks like this:</p> > <pre> > <xf:output value="serialize(.)" mediatype="text/plain"> > </xf:output> > </pre> > <hr/> > <p>These buttons apply the <code>setnode</code> action to the instance root (<code>.</code>):</p> > <xf:trigger> > <xf:label>Set Inner to '<item>b</item>'</xf:label> > <xf:setnode ref="." ev:event="DOMActivate" inner="'<item>b</item>'"/> > </xf:trigger> > <xf:trigger> > <xf:label>Set Outer to '<item>c</item>'</xf:label> > <xf:setnode ref="." ev:event="DOMActivate" outer="'<item>c</item>'"/> > </xf:trigger> > <hr/> > <p>These buttons apply the <code>setnode</code> action to the child of the instance root (<code>./item</code>):</p> > <xf:trigger> > <xf:label>Set Inner to '<item>d</item>'</xf:label> > <xf:setnode ref="./item" ev:event="DOMActivate" inner="'<item>d</item>'"/> > </xf:trigger> > <xf:trigger> > <xf:label>Set Outer to '<item>e</item>'</xf:label> > <xf:setnode ref="./item" ev:event="DOMActivate" outer="'<item>e</item>'"/> > </xf:trigger> > </body> > </html> > > The second button of the first pair attempts to supply an outer > value for the outermost element of the instance (.) -- essentially, > that is, it attempts to replace the instance entirely. The other > buttons work as I expected, but this one doesn't seem to have > the desired effect (and after I click it, the others stop working). > > My idea is that setnode ref="instance('x')" outer="..."/> > should have essentially the same effect as > > <xf:submit target="bounce-xml-string-back-as-xml" > replace="instance" instance="x" ref="y"/> > > when y is an XPath expression pointing to a node set whose > serialization is the same string as given in the 'outer' attribute. > > If that's not possible from an implementation point of view, I can > live with it. But I predict I won't be the last user who hopes it will > work that way :) > > In any case, thank you again, this is great. > I have fixed this now. It was due to metadata (model id, instance id) stored in extra attributes (xsltforms_*) being erased. Thank you for your feedbacks! -Alain |