Project

Profile

Help

Return XdmNode from .NET extension method

Added by Anonymous about 16 years ago

Legacy ID: #5621310 Legacy Poster: riko (reksteen)

Hi I am using saxon-b 9.1.03n and have followed the instructions in the documentation to create an extension function in .NET. I have got my extension running, and I am passing an XdmNode that I have loaded from an XML reader back. But when I run the xsl using saxon, instead of returning a node tree, if I do <xsl:copy-of select="myfunction(params)"/> it writes each element in the tree as &lt;ElementName&gt; instead of proper XML. Similarly, when I try to use an XPath on the result of the function it says that it isn't a node-set. Can somebody please tell me if I'm doing something wrong? I want to use my extension function to insert new XML into the document I am transforming. Thanks Riko


Replies (7)

Please register to reply

RE: Return XdmNode from .NET extension method - Added by Anonymous about 16 years ago

Legacy ID: #5622768 Legacy Poster: riko (reksteen)

If I use net.sf.saxon.om.NodeInfo instead of XdmNode I get the exception below: I assume it occurs because I am trying to insert new XML elements into the XSLT result while it is being transformed, and it doesn't know how to resolve the namespace of the new nodes? Any hints is to what I can do to get this to work? java.lang.IllegalArgumentException: Unknown name code 1857 at net.sf.saxon.om.NamePool.unknownNameCode(NamePool.java:834) at net.sf.saxon.om.NamePool.getDisplayName(NamePool.java:733) at net.sf.saxon.event.XMLEmitter.startElement(XMLEmitter.java:289) at net.sf.saxon.event.XMLIndenter.startElement(XMLIndenter.java:116) at net.sf.saxon.event.NamespaceReducer.startElement(NamespaceReducer.java:72) at net.sf.saxon.event.ComplexContentOutputter.startContent(ComplexContentOutputter.java:529) at net.sf.saxon.tinytree.TinyElementImpl.copy(TinyElementImpl.java:342) at net.sf.saxon.tinytree.TinyDocumentImpl.copy(TinyDocumentImpl.java:293) at net.sf.saxon.instruct.CopyOf.processLeavingTail(CopyOf.java:444) at net.sf.saxon.instruct.Choose.processLeavingTail(Choose.java:686) at net.sf.saxon.instruct.Template.applyLeavingTail(Template.java:203) at net.sf.saxon.instruct.ApplyTemplates.applyTemplates(ApplyTemplates.java:345) at net.sf.saxon.instruct.ApplyTemplates.apply(ApplyTemplates.java:213) at net.sf.saxon.instruct.ApplyTemplates.processLeavingTail(ApplyTemplates.java:174) at net.sf.saxon.instruct.Block.processLeavingTail(Block.java:556) at net.sf.saxon.instruct.Instruction.process(Instruction.java:93) at net.sf.saxon.instruct.ElementCreator.processLeavingTail(ElementCreator.java:296) at net.sf.saxon.instruct.Template.applyLeavingTail(Template.java:203) at net.sf.saxon.instruct.ApplyTemplates.applyTemplates(ApplyTemplates.java:345) at net.sf.saxon.instruct.ApplyTemplates$ApplyTemplatesPackage.processLeavingTail(ApplyTemplates.java:527) at net.sf.saxon.Controller.transformDocument(Controller.java:1812) at net.sf.saxon.Controller.transform(Controller.java:1621) at net.sf.saxon.Transform.processFile(Transform.java:1112) at net.sf.saxon.Transform.doTransform(Transform.java:719) at cli.Saxon.Cmd.DotNetTransform.Main(Unknown Source)

RE: Return XdmNode from .NET extension method - Added by Anonymous about 16 years ago

Legacy ID: #5623842 Legacy Poster: Michael Kay (mhkay)

I made an attempt to enable extension functions to recognize types in the Saxon.Api module such as XdmNode, but I hit a brick wall trying to make it work - I couldn't find any way of enabling a mutual dependency between the Java code in saxon9.dll and the C# code in Saxon.Api.dll. As far as I can see, there's no documentation suggesting this should be possible (but I could be wrong!). For the types that are recognized in calling extension functions, see http://www.saxonica.com/documentation/extensibility/converting-args.net.html This means that if you do return an XdmNode from an extension function, it will be treated as an "external object", which probably means that if you send it to the result tree, Saxon will call it's ToString method and write the resulting string. You may be better off constructing an XmlNode, Saxon should recognize this. The chances are that the "Unknown name code" exception is caused by having more than one NamePool present. A name code allocated by one name pool will not be recognized (or will be misrecognized) by a different name pool. Many Saxon interfaces include checks to prevent this happening, but extension functions are fairly "trusted" so their return values aren't necessarily checked very rigorously.

RE: Return XdmNode from .NET extension method - Added by Anonymous about 16 years ago

Legacy ID: #5626815 Legacy Poster: riko (reksteen)

Thanks. I found an example extension function in the latest saxon resources download that excepted and returned an XdmNode/XdmValue, but I could not run that example, as I wasn't able to build it. I sort of assumed (incorrectly I suppose) that meant I could get and return XdmNodes in my extension functions. Anyway, I'm fine returning and accepting NodeInfo objects instead, it's just that it won't work with the name pool at the moment. I considered using wrapped XmlNodes, but the restriction that it must come from the same XmlDocument as the original is an obstacle to me. I have an external service that produces well-formed xml, and I want to write an extension function that calls the service and puts the resulting xml into the transformartion with an xsl:copy-of. Would I be able to do this with SetParameter instead, or is that also intended only for value types and wrapped classes? /// <summary> /// Accept a node and an atomic value and return the sequence containing the string /// value of the node followed by the value. /// </summary> public static XdmValue combine(XdmNode node, XdmAtomicValue value) { ArrayList list = new ArrayList(); list.Add(new XdmAtomicValue(node.StringValue)); list.Add(value); return new XdmValue(list); }

RE: Return XdmNode from .NET extension method - Added by Anonymous about 16 years ago

Legacy ID: #5630764 Legacy Poster: Michael Kay (mhkay)

>I found an example extension function in the latest saxon resources download that excepted and returned an XdmNode/XdmValue, but I could not run that example Yes: it seems I misinformed you (and made the mistake of believing the documentation...). That example is working for me, and I've now looked at the code and seen how I made it work, by distributing the logic between the Java and C# parts of the product. I am able to run the Examples.cs program, including the XsltExtensibility test, which invokes the method combine() in SampleExtensions.cs, which in turn both expects and returns instances of XdmValue and XdmNode. I suspect it will work only when the transformation is run from a C# application, however, and not when it is run by invoking Transform or Query from the command line. I compiled these modules as follows: csc /target:exe /win32icon:c:\MyDotNet\icons\csharp.ico /debug+ ^ /r:%DLLDIR%/IKVM.OpenJDK.ClassLibrary.dll;%DLLDIR%/saxon9api.dll ^ /out:%DLLDIR%/samples/Examples.exe ^ Examples.cs csc /target:library /debug+ ^ /r:%DLLDIR%/IKVM.OpenJDK.ClassLibrary.dll;%DLLDIR%/saxon9.dll;%DLLDIR%/saxon9api.dll ^ /out:%DLLDIR%/samples/SampleExtensions.dll ^ SampleExtensions.cs Regards, Michael Kay http://www.saxonica.com/

RE: Return XdmNode from .NET extension method - Added by Anonymous about 16 years ago

Legacy ID: #5634585 Legacy Poster: riko (reksteen)

Aaah, that was probably my problem - I first tested my stylesheet by using Transform from the command line. I have now coded up the transformation, and I succesfully return the XdmNode in my external function, but I get the exception "Unknown name code 1857" which is exactly the same error I got when passing back a NodeInfo object in the earlier test (using Transform). So I assume it is now correctly trying to convert my XdmNode object to a node set, but because the nodes I am passing in are not part of the original document the transfomer doesn't like it for some reason? Is there a way I can get around this problem? Thanks again for all your help so far. Riko

RE: Return XdmNode from .NET extension method - Added by Anonymous about 16 years ago

Legacy ID: #5634788 Legacy Poster: Michael Kay (mhkay)

>Unknown name code 1857 As mentioned in my previous response, this probably suggests that you created the node under the wrong NamePool - that is, under a different Saxon.Api.Processor. You will need to make the original Processor known to the class containing the extension function. Unfortunately this means you might need to use an instance-level method rather than a static method. I appreciate this might be awkward. One workaround might be to create a new Processor in your extension method, and then change its NamePool to be the same as the current NamePool. I don't think that the documentation mentions that a method can have a first parameter of type net.sf.saxon.expr.XPathContext, and you can then get the current NamePool using context.getConfiguration().getNamePool(); you can then make your new Processor use this by ((net.sf.saxon.Configuration)Processor.Implementation).setNamePool(namePool); Another possibility (perhaps better) is to save a reference to the Processor in the Controller, where the extension method can find it. From the XsltTransformer, the Controller is available as the Implementation property; it has a method setUserData() that allows you to store anything you like; and you can retrieve this by again declaring the net.sf.saxon.expr.XPathContext parameter, and then doing context.getController().getUserData(). I do wonder whether it might be worth taking a step back. Why are you using an extension function in the first place? Are you sure it isn't doing something that could be better done in native XSLT code?

RE: Return XdmNode from .NET extension method - Added by Anonymous about 16 years ago

Legacy ID: #5634831 Legacy Poster: riko (reksteen)

Thanks for the reply. I actually just got this to work by doing exactly what you suggested - sharing the processor from the controller that does the transformation, so that the extension function can pick it up as well. I figured this makes sense from looking at the documentation for the name pool which suggest that each Configuration (= Processor in .NET) has it's own namepool. I realise that this seems like a strange thing to be doing, but I have a database service that returns the XML to me that I should include in my transformed xml (in short, I am picking up a date from the source xml, and with that date I need to call something in the db which then returns the relevant xml for that date). So doing it in native XSLT is a bit hard :-) But I am very glad that this is working now, all the help is much appreciated.

    (1-7/7)

    Please register to reply