Results 1 to 7 of 7

Thread: Need help with XPathTemplate and NodeMapper

  1. #1
    Join Date
    Sep 2004
    Posts
    127

    Default Need help with XPathTemplate and NodeMapper

    Hi,

    I'm extracting data from an XML payload using XPathTemplate, and would like to hand off nodes of that payload to another method for data extraction (again using XPathTemplate).

    This should be simple, but for some reason I cannot get it to work. It's probably something obvious, otherwise I would have found out by now..

    I attached a self-contained test case to reproduce my problem. The problem is that method handleSecondElement() does not extract the value of element thirdElement as expected.

    Some kind soul please help me out with this, because I've already spent way too much time on this..

    Thanks,
    Dan


    Code:
    import java.io.ByteArrayOutputStream;
    import java.io.StringReader;
    import java.util.Properties;
    
    import javax.xml.parsers.DocumentBuilderFactory;
    import javax.xml.transform.OutputKeys;
    import javax.xml.transform.Result;
    import javax.xml.transform.Source;
    import javax.xml.transform.Transformer;
    import javax.xml.transform.TransformerFactory;
    import javax.xml.transform.dom.DOMSource;
    import javax.xml.transform.stream.StreamResult;
    
    import org.springframework.xml.xpath.Jaxp13XPathTemplate;
    import org.springframework.xml.xpath.NodeMapper;
    import org.springframework.xml.xpath.XPathOperations;
    import org.w3c.dom.DOMException;
    import org.w3c.dom.Document;
    import org.w3c.dom.Node;
    import org.xml.sax.InputSource;
    
    public class Test {
    
        public static final String XML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" 
            + "<exampleRequest xmlns=\"http://com.example/schemas/test\">\n" 
            + "    <firstElement>Hi there!</firstElement>\n" 
            + "    <secondElement>\n" 
            + "        <thirdElement>Hello again!</thirdElement>\n" 
            + "    </secondElement>\n" 
            + "    <secondElement>\n" 
            + "        <thirdElement>And again!</thirdElement>\n" 
            + "    </secondElement>\n" 
            + "</exampleRequest>";
    
        public XPathOperations xpathTemplate;
    
        // set up and run test
        public Test() throws Exception {
            Properties props = new Properties();
            props.setProperty("tns", "http://com.example/schemas/test");
            Jaxp13XPathTemplate jaxp13XPathTemplate = new Jaxp13XPathTemplate();
            jaxp13XPathTemplate.setNamespaces(props);
            xpathTemplate = jaxp13XPathTemplate;
    
            Source exampleRequest = createSource(XML);
            handleExampleRequest(exampleRequest);
        }
    
        // create a source from the xml string
        public Source createSource(String xml) throws Exception {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            dbf.setNamespaceAware(true);
            Document doc = dbf.newDocumentBuilder().parse(new InputSource(new StringReader(xml)));
            return new DOMSource(doc);
        }
    
        // expects a exampleRequest element, extracts the firstElement element and
        // passes the secondElement elements to handleSecondElement()
        public void handleExampleRequest(Source exampleRequest) {
            System.out.println("\n\nHandling ExampleRequest:\n" + format(exampleRequest));
    
            String firstElement = xpathTemplate.evaluateAsString("/tns:exampleRequest/tns:firstElement", exampleRequest);
            System.out.println("found firstElement: " + firstElement);
    
            xpathTemplate.evaluate("/tns:exampleRequest/tns:secondElement", exampleRequest, new NodeMapper() {
    
                public Object mapNode(Node node, int num) throws DOMException {
                    Source secondElement = new DOMSource(node);
                    handleSecondElement(secondElement);
                    return null;
                }
            });
        }
    
        // expects a secondElement element and extracts the thirdElement element.
        // this is the part that does not work as expected.
        public void handleSecondElement(Source secondElement) {
            System.out.println("\n\nHandling SecondElement:\n" + format(secondElement));
            String thirdElement = xpathTemplate.evaluateAsString("/tns:secondElement/tns:thirdElement", secondElement);
            System.out.println("found thirdElement: " + thirdElement);
        }
    
        public static void main(String[] args) throws Exception {
            new Test();
        }
    
        // used for debugging only
        public String format(Source s) {
            try {
                TransformerFactory transformerFactory = TransformerFactory.newInstance();
                Transformer transformer = transformerFactory.newTransformer();
                transformer.setOutputProperty(OutputKeys.INDENT, "yes");
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                Result output = new StreamResult(bos);
                transformer.transform(s, output);
                return bos.toString().trim();
            }
            catch (Exception e) {
                System.out.println(e);
            }
            return "";
        }
    
    }

  2. #2
    Join Date
    Jul 2005
    Location
    Rotterdam, the Netherlands
    Posts
    1,562

    Default

    Could you try "tns:secondElement/tns:thirdElement" as expression in the handleSecondElement method and see if that works?

    The problem is probably that you start the expression with a slash, the root of the document. So even if you pass in the secondElement Element, the expression will still be calculated from the root. Removing the slash solves it (I hope).
    Arjen Poutsma

    Spring Web Services Dev Lead
    Please read the FAQ

  3. #3
    Join Date
    Sep 2004
    Posts
    127

    Default

    Nope, "tns:secondElement/tns:thirdElement" doesn't work either (no result).

    A workaround is to pass variable num through to handleSecondElement() from mapNode(), and then use "/tns:exampleRequest/tns:secondElement["+(num+1)+"]/tns:thirdElement". Not too elegant, and it wouldn't work with compiled XPath expressions.

    I just confirmed that node's getOwnerDocument() method returns the original document. So if XPath works on the document (and not the source it is passed), that would make some sense.

  4. #4
    Join Date
    Jul 2005
    Location
    Rotterdam, the Netherlands
    Posts
    1,562

    Default

    And how about just tns:thirdElement? Given that it operates with tns:secondElement as context, that should work...
    Arjen Poutsma

    Spring Web Services Dev Lead
    Please read the FAQ

  5. #5
    Join Date
    Sep 2004
    Posts
    127

    Default

    Thanks Arjen, "tns:thirdElement" does the trick (and accordingly, "@attrName" selects the value of secondElement's attrName attribute).

    Now if you could explain to me why that is so, I'd be one happy camper! The solution looks innocent enough, but then, using just "tns:firstElement" in handleExampleRequest() does not work.

    Is this simply how the DOM works? Or is it a quirk within SWS (I assume not)? Are there any tools worth looking at? XMLSpy gets good reviews, but seems to be Windows only

    Anyway, thanks for your constant support, it is much appreciated!

    Cheers,
    Dan

  6. #6
    Join Date
    Jul 2005
    Location
    Rotterdam, the Netherlands
    Posts
    1,562

    Default

    Well, XPath expression always operate on a context node, i.e. the context parameter in the XPathTemplate. Typically, that node is the document root node. If you use a "relative" expression (without a / at the beginning), it will start calculating from that point.

    Just think of it as a absolute vs relative paths on a filesystem. When you are in /home/arjen, and you do a "ls work", you will get the contents on the work directory. The same can be done with "ls /home/arjen/work", but that's absolute.
    Arjen Poutsma

    Spring Web Services Dev Lead
    Please read the FAQ

  7. #7
    Join Date
    Sep 2004
    Posts
    127

    Default

    Thanks, Arjen, I finally figured it out.

    In order to implement both handleExampleRequest() and handleSecondElement() symmetrically, I'm creating the source passed to handleExampleRequest() from the document's child node that is the document element of the document (instead of the document itself).

    Therefore, both methods now work with elements of the document, and not the document itself.

    I attached my example hoping it might be useful for someone else. The important part is the call to getDocumentElement() in the last line of createSource().

    Cheers,
    Dan



    Code:
    import java.io.ByteArrayOutputStream;
    import java.io.StringReader;
    import java.util.Properties;
    
    import javax.xml.parsers.DocumentBuilderFactory;
    import javax.xml.transform.OutputKeys;
    import javax.xml.transform.Result;
    import javax.xml.transform.Source;
    import javax.xml.transform.Transformer;
    import javax.xml.transform.TransformerException;
    import javax.xml.transform.TransformerFactory;
    import javax.xml.transform.dom.DOMSource;
    import javax.xml.transform.stream.StreamResult;
    
    import org.springframework.xml.xpath.Jaxp13XPathTemplate;
    import org.springframework.xml.xpath.NodeMapper;
    import org.springframework.xml.xpath.XPathOperations;
    import org.w3c.dom.DOMException;
    import org.w3c.dom.Document;
    import org.w3c.dom.Node;
    import org.xml.sax.InputSource;
    
    public class Test {
    
        public static final String XML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" 
            + "<exampleRequest xmlns=\"http://com.example/schemas/test\">\n" 
            + "    <firstElement>Hi there!</firstElement>\n" 
            + "    <secondElement someAttribute=\"firstAttributeValue\">\n" 
            + "        <thirdElement>Hello again!</thirdElement>\n" 
            + "    </secondElement>\n" 
            + "    <secondElement someAttribute=\"secondAttributeValue\">\n" 
            + "        <thirdElement>And again!</thirdElement>\n" 
            + "    </secondElement>\n" 
            + "</exampleRequest>";
    
        public XPathOperations xpathTemplate;
    
        // set up and run test
        public Test() throws Exception {
            Properties props = new Properties();
            props.setProperty("tns", "http://com.example/schemas/test");
            Jaxp13XPathTemplate jaxp13XPathTemplate = new Jaxp13XPathTemplate();
            jaxp13XPathTemplate.setNamespaces(props);
            xpathTemplate = jaxp13XPathTemplate;
    
            Source exampleRequest = createSource(XML);
            handleExampleRequest(exampleRequest);
        }
    
        // create a source from the xml string
        public Source createSource(String xml) throws Exception {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            dbf.setNamespaceAware(true);
            Document doc = dbf.newDocumentBuilder().parse(new InputSource(new StringReader(xml)));
            return new DOMSource(doc.getDocumentElement());
        }
    
        // expects a exampleRequest element, extracts the firstElement element and
        // passes the secondElement elements to handleSecondElement()
        public void handleExampleRequest(Source exampleRequest) {
            System.out.println("\n\nHandling ExampleRequest:\n" + format(exampleRequest));
    
            String firstElement = xpathTemplate.evaluateAsString("tns:firstElement", exampleRequest);
            System.out.println("found firstElement: " + firstElement);
            
            xpathTemplate.evaluate("tns:secondElement", exampleRequest, new NodeMapper() {
                public Object mapNode(Node node, int num) throws DOMException {
                    Source secondElement = new DOMSource(node);
                    handleSecondElement(secondElement);
                    return null;
                }
            });
        }
    
        // expects a secondElement element and extracts the thirdElement element.
        // this is the part that does not work as expected.
        public void handleSecondElement(Source secondElement) {
            System.out.println("\n\nHandling SecondElement:\n" + format(secondElement));
            String thirdElement = xpathTemplate.evaluateAsString("tns:thirdElement", secondElement);
            System.out.println("found thirdElement: " + thirdElement);
    
            String someAttribute = xpathTemplate.evaluateAsString("@someAttribute", secondElement);
            System.out.println("found someAttribute: " + someAttribute);
        }
    
        public static void main(String[] args) throws Exception {
            new Test();
        }
    
        // used for debugging only
        public String format(Source source) {
            ByteArrayOutputStream bytes = new ByteArrayOutputStream();
            Result result = new StreamResult(bytes);
            try {
                Transformer transformer = TransformerFactory.newInstance().newTransformer();
                transformer.setOutputProperty(OutputKeys.INDENT, "yes");
                transformer.transform(source, result);
            }
            catch (TransformerException e) {
                System.out.println(e);
            }
            return bytes.toString().trim();
        }
    
    }

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •