Announcement Announcement Module
Collapse
No announcement yet.
Need help with XPathTemplate and NodeMapper Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • 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
    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).

    Comment


    • #3
      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.

      Comment


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

        Comment


        • #5
          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

          Comment


          • #6
            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.

            Comment


            • #7
              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();
                  }
              
              }

              Comment

              Working...
              X