XML Data Object - TinyTree
Added by Anonymous about 17 years ago
Legacy ID: #4525373 Legacy Poster: Jocelyn Raymond (jraymond67)
Hi, I am trying to understand how some of the Saxon Java API works. We have purchased saxon-8.9.0.4 which I believe is the latest release. I have gone through the saxon-resources java examples to learn more about how to use the API. ---------------------- What I am trying to do ---------------------- I want to create an xml data object in java which would contain methods to modify and query the data model. I do understand that using the TinyTree Model we cannot "update" the tree. So what I have been trying to exploit is the following snippet (full code further below): TinyBuilder builder = new TinyBuilder(); XQueryExpression exp = this.sqc.compileQuery(xquery); // where sqc is a StaticQueryContext DynamicQueryContext dqc = new DynamicQueryContext(this.config); // this.config is a "new" Configuration that was instantiated during construction. dqc.setContextItem(this.doc) // this.doc is a NodeInfo see constructor below exp.run(dqc,builder,new Properties()); this.doc = builder.getCurrentRoot(); // Once I assign a new TinyTree, other Xquery do not work anymore (see error below) Hopefully, you will see that what I want to do is to keep an TinyTree as the model for the data. The data model can be "modified" by simply re-building the TinyTree using XQuery and even transform. This data object is passed through several process that can query and modify the data along the way. I only want to "serialize" at the end of my system which is why I would like to keep the model as a Tree and not a String. Hopefully, I am making some sense :-) Please let me know if this is a crazy idea or if there is a simpler method to do this. ---------------------- Error message: ---------------------- update done Error on line 1 of module with no systemId: XPDY0050: The root of the tree containing the context item is not a document node The root of the tree containing the context item is not a document node ; SystemID: module with no systemId; Line#: 1; Column#: -1 net.sf.saxon.trans.DynamicError: The root of the tree containing the context item is not a document node at net.sf.saxon.expr.Expression.dynamicError(Expression.java:780) at net.sf.saxon.expr.RootExpression.getNode(RootExpression.java:73) at net.sf.saxon.expr.SingleNodeExpression.iterate(SingleNodeExpression.java:113) at net.sf.saxon.expr.PathExpression.iterate(PathExpression.java:826) at net.sf.saxon.expr.Expression.process(Expression.java:346) at net.sf.saxon.query.XQueryExpression.run(XQueryExpression.java:346) at ca.ualberta.registrar.data.XMLData.applyXQuery(Unknown Source) at ca.ualberta.registrar.data.XMLData.serialize(Unknown Source) at ca.ualberta.registrar.data.XMLData.main(Unknown Source) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.apache.tools.ant.taskdefs.ExecuteJava.run(ExecuteJava.java:193) at org.apache.tools.ant.taskdefs.ExecuteJava.execute(ExecuteJava.java:130) at org.apache.tools.ant.taskdefs.Java.run(Java.java:705) at org.apache.tools.ant.taskdefs.Java.executeJava(Java.java:177) at org.apache.tools.ant.taskdefs.Java.execute(Java.java:83) at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:275) at org.apache.tools.ant.Task.perform(Task.java:364) at org.apache.tools.ant.Target.execute(Target.java:341) at org.apache.tools.ant.Target.performTasks(Target.java:369) at org.apache.tools.ant.Project.executeTarget(Project.java:1214) at org.apache.tools.ant.Project.executeTargets(Project.java:1062) at org.apache.tools.ant.Main.runBuild(Main.java:673) at org.apache.tools.ant.Main.startAnt(Main.java:188) at org.apache.tools.ant.Main.start(Main.java:151) at org.apache.tools.ant.Main.main(Main.java:241) -------------------------------------- Here is the code for the entire class: // packages and import were left out -------------------------------------- Look at the main method to see the test of this data object. Running the main method gives the error shown above. public class XMLData { public static final net.sf.saxon.TransformerFactoryImpl factory = new net.sf.saxon.TransformerFactoryImpl(); // We are Saxon specific private Configuration config = new Configuration(); // as long this object is alive, we will use one configuration object. private NodeInfo doc = null; private StaticQueryContext sqc = null; public static final String OMIT_XML_DECLARATION_Yes = "yes"; public static final String OMIT_XML_DECLARATION_No = "no"; // C O N S T R U C T O R S private XMLData(){}; // we do not want empty xml data model. // @XMLData(String xml) // We can construct an XMLData based on a xml string. public XMLData(String xml) throws Exception { this.sqc = new StaticQueryContext(this.config); this.doc = this.config.buildDocument(new StreamSource(new StringReader(xml))); } // @serialize // Simply output a String representation of the xml model i.e. the string will contain tags and all. public String serialize(String omitXmlDeclaration) throws Exception { return this.applyXQuery("/node()", omitXmlDeclaration); } // @transform // applies a transformation on the current xml model. The current xml model is replaced with the result of the transformation. public void transform(Transformer transformer) throws Exception { } // @applyXPath // applies the xpath expression on the current xml model. A String value which is the concatenation of all TextNode is returned. // The current xml data model should not be modified. public String applyXPath(String xpath) throws Exception { return null; } // @applyXQuery // applies the xquery expression on the current xml model and the "xml" result is returned as a String. // The current xml data model should not be modified. // Yes assume that the xquery will result into another xml structure. public String applyXQuery(String xquery, String omitXmlDeclaration) throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); final XQueryExpression exp = this.sqc.compileQuery(xquery); final DynamicQueryContext dqc = new DynamicQueryContext(this.config); final Properties props = new Properties(); props.setProperty(OutputKeys.METHOD, "xml"); props.setProperty(OutputKeys.INDENT, "no"); props.setProperty(OutputKeys.OMIT_XML_DECLARATION, (omitXmlDeclaration.equals(XMLData.OMIT_XML_DECLARATION_Yes) ? "yes" : "no")); dqc.setContextItem(this.doc); exp.run(dqc, new StreamResult(baos),props); return baos.toString(); } // @updateUsingXquery // The result of the xquery is used to re-build the current model i.e. re-build a TinyTree. public void updateUsingXQuery(String xquery) throws Exception { TinyBuilder builder = new TinyBuilder(); final XQueryExpression exp = this.sqc.compileQuery(xquery); final DynamicQueryContext dqc = new DynamicQueryContext(this.config); final Properties props = new Properties(); props.setProperty(OutputKeys.METHOD, "xml"); props.setProperty(OutputKeys.INDENT, "no"); dqc.setContextItem(this.doc); exp.run(dqc,builder,props); this.doc = builder.getCurrentRoot(); } public static void main(String args[]){ try{ String xml = "<?xml version='1.0'?><person><name><firstname>Homer</firstname><lastname>Simpson</lastname></name><gender>M</gender><birthdate>2000-01-25</birthdate></person>"; XMLData person = new XMLData(xml); String xquery = "for $doc in /node() return <person><name><firstname>OJ</firstname><lastname>Samson</lastname></name>{$doc/gender}{$doc/birthdate}</person>"; person.updateUsingXQuery(xquery); System.out.println("update done"); System.out.println(person.serialize(XMLData.OMIT_XML_DECLARATION_No)); }catch (Exception ex){ System.out.println(ex.getMessage()); ex.printStackTrace(); } } }
Replies (5)
Please register to reply
RE: XML Data Object - TinyTree - Added by Anonymous about 17 years ago
Legacy ID: #4525412 Legacy Poster: Jocelyn Raymond (jraymond67)
I would like to stress one point on the above idea. The way I understand this is that when exp.run(dqc,builder,props); is executed, the object builder (TinyBuilder) contains a new "modified" TinyTree. I noticed that it works only when I explicitly re-build the builder as follow: this.doc = this.config.buildDocument(builder.getCurrentRoot()); Could anyone give me any insight why is that? I would like to avoid building the tree twice. Thank you very much, Jocelyn Raymond
RE: XML Data Object - TinyTree - Added by Anonymous about 17 years ago
Legacy ID: #4525489 Legacy Poster: Michael Kay (mhkay)
I think the basic problem is that the result of your query is a tree rooted at an element node rather than a document node. If you use a path expression beginning with "/" on such a tree you get an error, because such expressions work only on trees rooted at document nodes. To create a document node, using a construct such as document { <doc> { for $x in... return <e/> } </doc> } Sending the result to a Builder should work fine, though it's a somewhat low-level interface so there are a number of things that could go wrong. I would advise using the Builder once only. A cleaner way to run a pipeline of queries is to evaluate each query using the iterator() method instead of the run() method. If the query returns a single node, then a call on next() will return this node, and you can then pass this node as the context item to the next query in the pipeline. If you use iterator() to execute the query, Saxon will build the tree representation of any nodes in the result for you, so you don't need to use low-level classes like Builder to do the job yourself.
RE: XML Data Object - TinyTree - Added by Anonymous about 17 years ago
Legacy ID: #4525525 Legacy Poster: Jocelyn Raymond (jraymond67)
Thanks Michael, I will try your suggestion (I must admit, I have to think carefully about exactly what you have proposed). In the meantime I have a quick question regarding what you proposed above and my initial intention (see above). What about when I will need to do a transform? For instance: TinyBuilder builder = new TinyBuilder(); transformer.transform(this.doc, builder); Can I assume that builder.getCurrentRoot() is the "tranformed" tree? Can it be used to replace my current reference to my data model? i.e. this.doc = builder.getCurrentRoot(); Note that I have to educate myself about what you said "tree rooted at an element node" rather "tree rooted at a document node" which I am sure will help in understanding your answer. Thanks again for your help Jocelyn Raymond
RE: XML Data Object - TinyTree - Added by Anonymous about 17 years ago
Legacy ID: #4526996 Legacy Poster: Michael Kay (mhkay)
For running XSLT transformations in a pipeline, you can use the JAXP mechanisms - you'll find examples in the TraxExamples sample application. In fact JAXP offers two mechanisms; see the SAXTransformerFactory class and its newTransformerHandler() and new XMLFilter() methods. Saxon also has a TransformerReceiver class, which is similar to the JAXP TransformerHandler except that it implements Saxon's Receiver interface rather than the SAX ContentHandler, which is marginally more efficient and also allows type annotations to be passed through.
RE: XML Data Object - TinyTree - Added by Anonymous about 17 years ago
Legacy ID: #4527070 Legacy Poster: Jocelyn Raymond (jraymond67)
Hi, Thank again. I will look and run the TraxExamples. I did try your suggestion above using document {...} to get a tree rooted at the document node and it worked. Thank you very much for your help, Jocelyn Raymond
Please register to reply