Project

Profile

Help

Help with ExtensionInstruction

Added by Anna Benton over 9 years ago

Hi,

I'm working on an http extension instruction and am having problems passing through the data that actually needs to be posted for POST instructions. I need to be able to take anything, process all the xsl parts (like xsl:value-of) and then take the rest as-is (plain text or xml) and post it to a server. My problem is that the xml tagging is being stripped out of the entity I need to post. A slight complication is that I need to be agnostic about what I'm posting, this example contains db:batch but not all of my use cases will, db:batch is something the server I'm posting to needs to understand, not something I need to care about.

I've simplified my use case for the example below.

From my stylesheet, the http:post extension instruction: <http:post href="{$target-url}" content-type="application/xml"> <db:batch dbname="{$dbname}"> db:query db:sqlcreate table <xsl:value-of select="$table-name"/> (resource_key int)</db:sql> </db:query> db:query db:sql select * from <xsl:value-of select="$table-name"/> </db:sql> </db:query> db:query db:sqldrop table <xsl:value-of select="$table-name"/></db:sql> </db:query> </db:batch> </http:post>

The node obtained through the axisIterator at compile looks like this: <db:batch xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:http="http://www.highwire.org/saxon/http" xmlns:db="http://schema.highwire.org/SQL/batch" dbname="{$dbname}"> db:query db:sqlcreate table <xsl:value-of select="$table-name"/> (resource_key int)</db:sql> </db:query> db:query db:sqlselect * from <xsl:value-of select="$table-name"/></db:sql> </db:query> db:query db:sqldrop table <xsl:value-of select="$table-name"/></db:sql> </db:query> </db:batch>

I make the expression from that node like this: Expression nodeEx = makeExpression(node.toString());

The expression looks like this: FixedElement(Block(FixedAttribute(string-join(convertItems(data(mergeAdjacentText($dbname))), " ")), FixedElement(FixedElement(Block(ValueOf("create table "), ValueOf(string-join(convertItems(data(mergeAdjacentText($$))), " ")), ValueOf(" (resource_key int)")))), FixedElement(FixedElement(Block(ValueOf(" select * from "), ValueOf(string-join(convertItems(data(mergeAdjacentText($$))), " "))))), FixedElement(FixedElement(Block(ValueOf("drop table "), ValueOf(string-join(convertItems(data(mergeAdjacentText($$))), " ")))))))

The resulting Sequence that gets passed as an argument to call looks like this (note all the tagging has been stripped out, but the table name has been added in which is good!):
create table #24071f68c4c (resource_key int) select * from #24071f68c4cdrop table #24071f68c4c

What I actually want is this (with all the db tags left in, serialized out as a string): <db:batch xmlns:db="http://schema.highwire.org/SQL/batch" dbname="reflinks"> db:query db:sqlcreate table #24071f68c4c (resource_key int)</db:sql> </db:query> db:query db:sqlselect * from #24071f68c4c</db:sql></db:query>db:querydb:sqldrop table #24071f68c4c</db:sql> </db:query> </db:batch>

Any ideas on how to preserve the tagging? I've been digging through the libraries and so far am having no luck.

Thanks!


Replies (4)

Please register to reply

RE: Help with ExtensionInstruction - Added by Michael Kay over 9 years ago

It looks to me as if you've got an expression which, if evaluated, will return exactly the node tree you are looking for (though I have to say, I don't fully understand where it came from). But somewhere along the line, the node tree returned by this expression is being flattened to a string. So it's a question of finding out where that is happening. How are you evaluating the expression?

RE: Help with ExtensionInstruction - Added by Anna Benton over 9 years ago

Actually, I'll correct my earlier statement, for this example I'm not using makeExpression, I'm doing this: Expression expression = ((StyleElement) nodeInfo).compile(exec, decl);

The expression is being passed to setArguments as part of the Expression[]. Evaluation happens outside of my code (I assume using whatever default there is for that type of expression) and then is passed in as part of the Sequence[] to my call function.

My code, in the hopes that it will provide some clarity: Here's my compile method: public final Expression compile(final Executable exec, final Declaration decl) throws XPathException {

   //Enable logging
    org.apache.log4j.BasicConfigurator.configure();
    Logger.getRootLogger().setLevel(org.apache.log4j.Level.DEBUG);

    final AxisIterator kids = iterateAxis(AxisInfo.CHILD);
   
    NodeInfo nodeInfo = (NodeInfo) kids.next();
    
    final XdmNode node = new XdmNode(nodeInfo);
    LOG.debug("original node is " + node.toString());
    Expression expression  = ((StyleElement) nodeInfo).compile(exec, decl);
    LOG.debug("my expression is " + expression.toString());
   
    return new PostInstruction(this.href, this.headers, this.wrap,
                                this.timeout, this.debug, expression);
}

The constructor for my simple expression: public PostInstruction(final Expression href, final Map<String, Expression> headers, final Expression wrap, final Expression timeout, final Expression debug, Expression nodeEx ) { Expression[] sub = {href, wrap, timeout, debug, nodeEx}; setArguments(sub); this.headers = headers; }

And then my call (trimmed down to only the relevant parts):

public Sequence call( final XPathContext context, final Sequence[] arguments) throws XPathException {

        // Handle attributes
        final String href = arguments[HREF].head().getStringValue();
        final String wrap = arguments[WRAP].head().getStringValue();
        final String timeout = arguments[TIMEOUT].head().getStringValue();
        final String debug = arguments[DEBUG].head().getStringValue();
       
       // Handle entity
        String nodeString = "";
        if(arguments[ENTITY] != null) {
            nodeString = arguments[ENTITY].head().getStringValue();
            LOG.debug("entity sequence as a string " + arguments[ENTITY].toString());
            LOG.debug("entity sequence head as a string " + nodeString);
        }
 }

The results of the logging statements when I run:

73 [main] DEBUG org.highwire.saxon.http.api.HTTPPost null - original node is <db:batch xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:http="http://www.highwire.org/saxon/http" xmlns:db="http://schema.highwire.org/SQL/batch" dbname="{$dbname}"> db:query db:sqlcreate table <xsl:value-of select="$table-name"/> (resource_key int)</db:sql> </db:query> db:query db:sql select * from <xsl:value-of select="$table-name"/> </db:sql> </db:query> db:query db:sqldrop table <xsl:value-of select="$table-name"/> </db:sql> </db:query> </db:batch> 76 [main] DEBUG org.highwire.saxon.http.api.HTTPPost null - my expression is FixedElement(Block(FixedAttribute(string-join(convertItems(data(mergeAdjacentText($dbname))), " ")), FixedElement(FixedElement(Block(ValueOf("create table "), ValueOf(string-join(convertItems(data(mergeAdjacentText($$))), " ")), ValueOf(" (resource_key int)")))), FixedElement(FixedElement(Block(ValueOf(" select * from "), ValueOf(string-join(convertItems(data(mergeAdjacentText($$))), " "))))), FixedElement(FixedElement(Block(ValueOf("drop table "), ValueOf(string-join(convertItems(data(mergeAdjacentText($$))), " "))))))) 230 [main] DEBUG org.highwire.saxon.http.api.HTTPPost null - entity sequence as a string net.sf.saxon.tree.tiny.TinyElementImpl@20400b 230 [main] DEBUG org.highwire.saxon.http.api.HTTPPost null - entity sequence head as a string create table #24071f68c4c (resource_key int) select * from #24071f68c4cdrop table #24071f68c4c

RE: Help with ExtensionInstruction - Added by Michael Kay over 9 years ago

I think the problem is here:

nodeString = arguments[ENTITY].head().getStringValue();

That call on getStringValue() is taking the node tree and giving you the string value of the node at the root of this tree, as if you called the string() function.

If you want the XML serialization of the node, ie. a string containing start and end tags, the simplest way is probably (and non-obviously) to extract the NodeInfo object, and pass it to the static method net.sf.saxon.query.QueryResult.serialize().

RE: Help with ExtensionInstruction - Added by Anna Benton over 9 years ago

Thank you, Dr. Kay, that worked perfectly!

    (1-4/4)

    Please register to reply