Home » Eclipse Projects » EclipseLink » MOXy XPath expression compatibility with DOM and javax.xml.xpath.XPath(MOXy has an excellent facility to traverse JAXB object trees, but there seem to be a few incompatibilities with how you would code XPaths for traversing a DOM)
MOXy XPath expression compatibility with DOM and javax.xml.xpath.XPath [message #1234724] |
Wed, 22 January 2014 12:06  |
Eclipse User |
|
|
|
MOXy has a useful mechanism to allow XPath expressions to be used on JAXB annotated POJO. However there some XPath expressions do not seem to function in the same way when used against a org.w3c.dom.Document or Node.
The problem is ofcourse that whereas an XPath constructed against a Node has access to the DOM, because a Node normally has a parent, there is no equivalent in a POJO containment hierarchy. In my examples, Child and Leaf have no 'back pointers' to their parents for example. So unless some context were kept from the unmarshalling on a File then there is no way to do '//x' (i.e. select all 'x's from the document element root) or '..'. If some context were kept then this would imply some sort of overhead, and when I am streaming I do not want that (see the restrictions that XSLT has in its streaming mode). If I am just using POJOs then I don't have any context anyway. So the 'edge cases' I encountered below do not seem feasible. The fact that '.' does not work is a bit disappointing though.
I have left this 'question' incase any other users are walking backwards into pojo navigation from DOM/XSLT and XPath like me.
1) A POJO hierarchy has no 'invisible' Document Element Root
2) Traversing 'outside' the current context.
3) Lack of support for '.'
I have included examples to show what I mean, if someone can tell me where I am going wrong, or whether there is any chance that changes to make things more compatible would ever be contemplated, I would be most grateful.
Here are the JAXB annotated POJOs. They are in the same package and are specified via jaxb.index
package uk.co.his.test.jaxb.model;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlAccessorType( XmlAccessType.FIELD )
@XmlRootElement(name="Root")
public class Root {
@XmlElement(name="name")
public String name;
@XmlElement(name="children")
List<Child> children = new ArrayList<Child>();
public void addChild(Child child) {
children.add(child);
}
public void printOut(PrintStream w) {
w.println("Root " + name);
for(Child c: children) {
c.printOut(w);
}
}
}
package uk.co.his.test.jaxb.model;
import java.io.PrintStream;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
@XmlAccessorType( XmlAccessType.FIELD )
@XmlType
public class Child {
@XmlElement(name="name")
public String name;
@XmlElement(name="Leaf")
public Leaf leaf;
public void printOut(PrintStream w) {
w.println("\tChild " + name);
if(leaf != null) {
leaf.printOut(w);
}
else {
w.println("\t\tNo Leaf");
}
}
}
package uk.co.his.test.jaxb.model;
import java.io.PrintStream;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
@XmlAccessorType( XmlAccessType.FIELD )
@XmlType
public class Leaf {
@XmlElement(name="name")
public String name;
public void printOut(PrintStream w) {
w.println("\t\tLeaf " + name);
}
}
And here is the test case;
package uk.co.his.test.jaxb.junit;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import junit.framework.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
import uk.co.his.test.jaxb.model.Child;
import uk.co.his.test.jaxb.model.Leaf;
import uk.co.his.test.jaxb.model.Root;
public class TestMoxy3 {
public static final File test1 = new File("test-output/Test1.xml");
@BeforeClass
public static void boostrap() throws JAXBException, IOException, SAXException, TransformerException {
createFile(test1);
}
@Test
public void testXPathAndMoxy() throws JAXBException, SAXException, IOException, ParserConfigurationException, XPathExpressionException {
org.eclipse.persistence.jaxb.JAXBContext c2 = createJAXBContext();
Unmarshaller u = c2.createUnmarshaller();
Root r2 = (Root) u.unmarshal(test1);
DocumentBuilder b = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document d = b.parse(test1);
XPath xp = XPathFactory.newInstance().newXPath();
System.out.println("========= TESTS ===============");
//Edge case (1) The current context in Moxy is already the instance of Root
String xPath = "Root/children[1]/Leaf/name/text()";
XPathExpression xpr = xp.compile(xPath);
String domResultStr = xpr.evaluate(d);
String jaxbResultStr = c2.getValueByXPath(r2, xPath, null, String.class);
System.out.println("1) " + xPath + " gives \"" + domResultStr + "\" using DOM and \""+ jaxbResultStr + "\" using Moxy");
//Edge case (2) Xpath expressions using javax.xml.xpath and DOM have a global scope regardless of current context
//First get some child somewhere in the middle of the hierarchy
String xPathA = "//children[1]";
xpr = xp.compile(xPathA);
Node domResultNode = (Node) xpr.evaluate(d, XPathConstants.NODE);
Child jaxbResultChild = c2.getValueByXPath(r2, xPathA, null, Child.class);
//Now find out whether we can navigate 'back up' the hierachy
xPath = "//children[2]/name/text()";
Assert.assertTrue(xPathA + " gave unexpected null result for javax.xml.xpath and DOM", domResultNode != null);
xpr = xp.compile(xPath);
domResultStr = xpr.evaluate(domResultNode);
Assert.assertTrue(xPathA + " gave unexpected null result for MOXy", jaxbResultChild != null);
jaxbResultStr = c2.getValueByXPath(jaxbResultChild, xPath, null, String.class);
System.out.println("2) " + xPath + " via " + xPathA + " gives \"" + domResultStr + "\" using DOM and \""+ jaxbResultStr + "\" using Moxy");
//Edge case (3) '/' means the Document Element Root in XPath, but evaluates to the current node in Moxy
xPath = "/name/text()";
xpr = xp.compile(xPath);
domResultStr = xpr.evaluate(domResultNode);
jaxbResultStr = c2.getValueByXPath(jaxbResultChild, xPath, null, String.class);
System.out.println("3) " + xPath + " via " + xPathA + " gives \"" + domResultStr + "\" using DOM and \""+ jaxbResultStr + "\" using Moxy");
//BUG (4) '.' means the current context in XPath, but not in MOXy
xPath = "./name/text()";
xpr = xp.compile(xPath);
domResultStr = xpr.evaluate(domResultNode);
jaxbResultStr = c2.getValueByXPath(jaxbResultChild, xPath, null, String.class);
System.out.println("4) " + xPath + " via " + xPathA + " gives \"" + domResultStr + "\" using DOM and \""+ jaxbResultStr + "\" using Moxy");
/* This works
xPath = "name/text()";
xpr = xp.compile(xPath);
domResultStr = xpr.evaluate(domResultNode);
jaxbResultStr = c2.getValueByXPath(jaxbResultChild, xPath, null, String.class);
System.out.println("4b) " + xPath + " via " + xPathA + " gives \"" + domResultStr + "\" using DOM and \""+ jaxbResultStr + "\" using Moxy");
*/
}
private static void createFile(File named) throws JAXBException, IOException, SAXException, TransformerException {
Root r = createRoot();
JAXBContext c1 = createJAXBContext();
Marshaller m = c1.createMarshaller();
m.marshal(r, test1);
System.out.println("===== Here is the target Object Hierachy =======");
r.printOut(System.out);
System.out.println("===== Here is resultant XML file contents =======");
echoFile(test1, System.out);
}
private static Root createRoot() {
Root r = new Root();
r.name = "RootA";
for (int i = 1; i <= 10; i++) {
Child c = new Child();
c.name = "A" + i;
Leaf l = new Leaf();
l.name = "a" + i;
c.leaf = l;
r.addChild(c);
}
return r;
}
public static void echoFile(File which, PrintStream out) throws SAXException, IOException, TransformerException
{
TransformerFactory transFactory = TransformerFactory.newInstance();
try
{
transFactory.setAttribute("indent-number", new Integer(2));
}
catch (IllegalArgumentException ignored) //If whatever we pick up doesn't support it, just carry on
{
System.err.println("Default TransformerFactory " + transFactory.getClass() + " does not support indent-number");
}
Transformer transformer = transFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
try
{
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
}
catch (IllegalArgumentException ignored) //If whatever we pick up doesn't support it, just carry on
{
System.err.println("Default TransformerFactory " + transFactory.getClass() + " does not support {http://xml.apache.org/xslt}indent-amount");
}
try
{
transformer.transform(new StreamSource(which), new StreamResult(out));
}
catch (TransformerException e)
{
throw e;
}
}
private static org.eclipse.persistence.jaxb.JAXBContext createJAXBContext() throws JAXBException {
return (org.eclipse.persistence.jaxb.JAXBContext) org.eclipse.persistence.jaxb.JAXBContextFactory.createContext("uk.co.his.test.jaxb.model", null);
}
}
Attachment: MoxyMix.zip
(Size: 14.84KB, Downloaded 248 times)
[Updated on: Wed, 22 January 2014 16:54] by Moderator
|
|
| | | |
Goto Forum:
Current Time: Wed Jul 23 17:49:31 EDT 2025
Powered by FUDForum. Page generated in 0.03009 seconds
|