0

I have two kind of files that can contain:

  1. A list of objects
  2. A single object

Example file for list of objects:

<?xml version='1.0' encoding='UTF-8'?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
    <S:Body>
        <ns2:getBeanListResponse xmlns:ns2="namespace">
            <beanObject>
                <fieldA>valueA</fieldA>
                <fieldB>valueB</fieldB>
                <fieldC>valueC</fieldC>
            </beanObject>
            <beanObject>
                <fieldA>valueD</fieldA>
                <fieldB>valueE</fieldB>
                <fieldC>valueF</fieldC>
            </beanObject>
            <beanObject>
                <fieldA>valueG</fieldA>
                <fieldB>valueH</fieldB>
                <fieldC>valueI</fieldC>
            </beanObject>
        </ns2:getBeanListResponse>
    </S:Body>
</S:Envelope>

Example file for single object:

<?xml version='1.0' encoding='UTF-8'?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
    <S:Body>
        <ns2:getBeanResponse xmlns:ns2="namespace">
            <beanObject>
                <fieldA>value1</fieldA>
                <fieldB>value2</fieldB>
                <fieldC>value3</fieldC>
            </beanObject>
        </ns2:getBeanResponse>
    </S:Body>
</S:Envelope>

I use XPathExpression to retrieve the object(s) and JaxB to convert the xml data into java objects. For the file with a list of Objects i used the expression

//beanObject

and had no problems so far.. JaxB successufully translates that xml data into java objects. However I do something wrong when parsing a single object.. I used the same xPathExpression but something goes wrong. Here is the function that is used for the list of objects:

public Object getNodeListValues(String expression) {
    NodeList resultNodes;
    
    try {
        XPathExpression xPathExpression = xpath.compile(expression);
        resultNodes = (NodeList) xPathExpression.evaluate(xmlDocument, XPathConstants.NODESET);
        
        JAXBContext context = JAXBContext.newInstance(BeanObject.class);
        Unmarshaller unmarshaller = context.createUnmarshaller();
        
        for (int i=0; i<resultNodes.getLength(); i++) {
            BeanObject obj = (BeanObject) unmarshaller.unmarshal(resultNodes.item(i));
            System.out.println(obj);
        }
    } catch (Exception e) {
        System.out.println(e.getMessage()); 
    }
    
    return null;
}

but I'm not sure on how to process a single bean object.. I tried with this function:

public Object getNodeValue(String expression) {
        Node resultNode = null;
        try {
            XPathExpression xPathExpression = xpath.compile(expression);
            resultNode = (Node) xPathExpression.evaluate(xmlDocument, XPathConstants.NODE);
            return resultNode.getText();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        
        return null;
    }

but that gives me an exception saying that the cast is not possible:

com.sun.org.apache.xerces.internal.dom.DeferredElementNSImpl cannot be cast to org.dom4j.Node

From this question I guess that the node result here is the text node which is the \n part, but it's not clear to me why the list of items works and this function doesn't.. How can I get all the values of that xml bean? Also assume that I don't know the structure or fields of the objects in the xml file.

Thanks

User1254
  • 85
  • 5

1 Answers1

1

You try to cast to org.dom4j.Node, instead of org.w3c.dom.Node. Change your import statements or use the fully qualified class name.

Edit: Since you probably don't need DOM4j in this class you should have:

import org.w3c.dom.Node;

in the preamble and your method should be:

    public String getNodeValue(String expression) {
        Node resultNode = null;
        try {
            XPathExpression xPathExpression = xpath.compile(expression);
            resultNode = (Node) xPathExpression.evaluate(xmlDocument, XPathConstants.NODE);
            // DOM4j and DOM have similar methods, but with different names.
            return resultNode.getTextContent();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        
        return null;
    }
Piotr P. Karwasz
  • 12,857
  • 3
  • 20
  • 43
  • I'm actually trying to cast to `org.dom4j.Node`, trying to cast to `org.w3c.dom.Node` gives a type mismatch error: `Type mismatch: cannot convert from org.w3c.dom.Node to org.dom4j.Node` – User1254 Jan 18 '21 at 08:13
  • Yes, that is the problem: there should be no `org.dom4j` in the `getNodeValue` function. Both the `resultNode` variable and the cast should be to the W3C interface `org.w3c.dom.Node`. In Java many classes have the same simple name, but you usually use only one of them. – Piotr P. Karwasz Jan 18 '21 at 09:11
  • Thank you, that actually worked. Didn't know about this difference, guess I'll have to look it up. – User1254 Jan 18 '21 at 11:21
  • It's an easy bug to miss, the editors take care of managing the `import` statements (and Eclipse's first choice is usually the wrong one :-) ). [DOM](https://en.wikipedia.org/wiki/Document_Object_Model) is a standard interface and in Java has an implementation embedded into the JRE (a copy of Xerces). [DOM4j](https://dom4j.github.io/) is an external library, which you probably have as a dependency of Hibernate. When in doubt choose DOM. – Piotr P. Karwasz Jan 18 '21 at 11:34