Java XML - Node Name and Value retrieval not as expected

Viewed 62

Using the following sample to parse an XML file. Able to get it working with samples taken from various places, but it doesn't explain to me why the following fails which is the objective of this post.

Here is the code being executed.

import java.io.IOException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class XMLQuestions   
{

    public static void main(String argv[]) 
    {
       try 
       {
        String filepath = "c:\\Downloads\\DummyData.xml";

        DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
        Document doc = docBuilder.parse(filepath);
        doc.setXmlStandalone(true);

        NodeList allDeliveryLocations = doc.getElementsByTagName("DeliveryLocations");
        for(int j=0; j < allDeliveryLocations.getLength();j++)
        {
            Element deliveryLocationElement = (Element) allDeliveryLocations.item(j);
            Node deliveryLocationNode = (Node) allDeliveryLocations.item(j);

            System.out.println("Get Element & Node Name");
            System.out.println("     deliveryLocationElement : " + deliveryLocationElement.getNodeName());
            System.out.println("     deliveryLocationNode    : " + deliveryLocationNode.getNodeName());
            System.out.println("");
            
            System.out.println("GetFirstChild()");
            Node deliveryLocationChild = deliveryLocationNode.getFirstChild();
            System.out.println("     Node Name  : " + deliveryLocationChild.getNodeName());
            System.out.println("     Node Value : " + deliveryLocationChild.getNodeValue() );
            System.out.println("");

        System.out.println("GetNextSibling()");
        deliveryLocationChild = deliveryLocationChild.getNextSibling();
        System.out.println("     Node Name  : " + deliveryLocationChild.getNodeName());
        System.out.println("     Node Value : " + deliveryLocationChild.getNodeValue());
        System.out.println("");
            
       }
        System.out.println("Done");
        
       } catch (ParserConfigurationException pce) {
        pce.printStackTrace();
       } 
       catch (IOException ioe) {
        ioe.printStackTrace();
       } catch (SAXException sae) {
        sae.printStackTrace();
       }
    }
}

Sample XML used for testing.

<?xml version="1.0" encoding="UTF-8"?>
<AllStorage>
  <NorthAmerica>
      <EastCoast>
        <DeliveryLocations>
        <Location>North East </Location>
          <Item1>Full</Item1>
          <Item2>Empty</Item2>
          <Item3 attr1="1" attr2="2" />
          <Item4 istransferable="true">
            <States>
              <State>
                <NewYork>
                  <UpTown>
                    <TimeOfDelivery>Morning</TimeOfDelivery>
                    <DeliveryLocation>back</DeliveryLocation>
                  </UpTown>
                </NewYork>
              </State>
              <State>
                <NewYork>
                  <UpTown>
                    <TimeOfDelivery>Evening</TimeOfDelivery>
                    <DeliveryLocation>side</DeliveryLocation>
                  </UpTown>
                </NewYork>
              </State>
              <State>
                <NewYork>
                  <UpTown>
                    <TimeOfDelivery>Afternoon</TimeOfDelivery>
                    <DeliveryLocation>front</DeliveryLocation>
                  </UpTown>
                </NewYork>
              </State>
            </States>
            </Item4>
        </DeliveryLocations>
        
        <DeliveryLocations>
        <Location>South East </Location>
          <Item1>Totally Full </Item1>
          <Item2>Half Empty</Item2>
          <Item3 attr1="5" attr2="6" />
          <Item4 istransferable="true">
            <States>
              <State>
                <Florida>
                  <UpTown>
                    <TimeOfDelivery>Early Morning</TimeOfDelivery>
                    <DeliveryLocation>front</DeliveryLocation>
                  </UpTown>
                </Florida>
              </State>
              <State>
                <Florida>
                  <UpTown>
                    <TimeOfDelivery>MidDay</TimeOfDelivery>
                    <DeliveryLocation>back</DeliveryLocation>
                  </UpTown>
                </Florida>
              </State>
              <State>
                <Florida>
                  <UpTown>
                    <TimeOfDelivery>Midnight</TimeOfDelivery>
                    <DeliveryLocation>back</DeliveryLocation>
                  </UpTown>
                </Florida>
              </State>
            </States>
            </Item4>
        </DeliveryLocations>
      </EastCoast>
  </NorthAmerica>
</AllStorage>

Executing these lines, helps to see that either an Element or Node can be used.

        Element deliveryLocationElement = (Element) allDeliveryLocations.item(j);
        Node deliveryLocationNode = (Node) allDeliveryLocations.item(j);

Here is the output generated from the above.

enter image description here

Question 1 :

What determines whether to use an Element or Node? Would it be what ever methods one expects to execute or data to retrieve?

Next, the following lines of code are executed.

        System.out.println("GetFirstChild()");
        Node deliveryLocationChild = deliveryLocationNode.getFirstChild();
        System.out.println("     Node Name  : " + deliveryLocationChild.getNodeName());
        System.out.println("     Node Value : " + deliveryLocationChild.getNodeValue() );
        System.out.println("");

Here is the output generated.

enter image description here

Since Location is the child of DeliveryLocations, I was expected to see

 Node Name : Location
 Node Value : North East

Question 2 :

Why didn't getFirstChild() return Location as expected? Is there an additional call required?

Next, the following lines of code are executed.

        System.out.println("GetNextSibling()");
        deliveryLocationChild = deliveryLocationChild.getNextSibling();
        System.out.println("     Node Name  : " + deliveryLocationChild.getNodeName());
        System.out.println("     Node Value : " + deliveryLocationChild.getNodeValue());
        System.out.println("");

Here is the output generated.

enter image description here

Ok, this time I get Location printed out, but it was after a getNextSibling() and still the value is null.

Question 3 :

Why was it required to call both getFirstChild() and getNextSibling() in order to see the first chile?

Why didn't getNextSibling() print out Item1 instead of null?

I think getting the answers to these question will help with a better understanding of what is happening and why certain calls are required.

2 Answers

It seems the first question you should consider is this:

What is the Document Object Model (DOM) and how does it relate to my findings in below?

That answer is found in the formal specification of the DOM

Paraphrasing that reference, one finds:

the nodes in the [DOM] do not represent a data structure, they represent objects, which have functions and identity

We also find a glossary entry for element:

Each document contains one or more elements, the boundaries of which are either delimited by start-tags and end-tags, or, for empty elements by an empty-element tag. Each element has a type, identified by name, and may have a set of attributes. Each attribute has a name and a value. See Logical Structures in XML [XML 1.0].

The term Document Object Model suggests the DOM is object oriented. As such, it turns out, a Document is a Node that has Elements that are also Nodes.

With this background, we can see that the actual DOM manipulation takes place upon the API provided by the Node interface and the actual structure the XML tags can be discovered using the Element interface.

Now your questions are easy to answer:

Question 1 : What determines whether to use an Element or Node?

  1. if you want to manipulate the DOM, use a Node
  2. if you want to get attributes from tags use an Element
  3. Since Element is a Node you can just use Element for everything

Question 2 : Why didn't getFirstChild() return Location as expected?

The first child of that Node is a TextNode - Every Node can potentially have text between the opening/closing tags of its Element. So, each Node has a child node that is a Text Node ref1, ref2

Question 3 : Why was it required to call both getFirstChild() and getNextSibling() in order to see the first child?

it isn't. All you did is traverse the DOM to get to the Node you expected to be the first Node - but as you now know, it wasn't.

Question 1 : What determines whether to use an Element or Node?

Every Element is a Node, but not every Node is an Element. Other kinds of node include text nodes, comment nodes, etc.

Question 2 : Why didn't getFirstChild() return Location as expected?

Because the tree also contains text nodes.

Question 3 : Why was it required to call both getFirstChild() and getNextSibling() in order to see the first child?

It isn't. The first child is a text node, the getNextSibling() gets you to the second child, which is an element node.

Related