Link to home
Start Free TrialLog in
Avatar of jsm11482
jsm11482

asked on

JDOM - finding a nested CHILD ELEMENT

Hi, I need help creating a loop that will find a child element ANYWHERE in the JDOM Document.  For instance, my document is as follows:
<ROOT>
  <E1>
    <E1A>TEXT AND SUCH</E1A>
    <E1B>
      <E1B1>text...</E1B1>
    </E1B>
  </E1>
</ROOT>
To find a first-level child is easy: ROOT.getChild("E1"); However, to find a nested child (E1B1 for instance) you need a loop to look through the root element for E1B1, if we dont find it there, look through all of the second level elements (E1A and E1B), and so on until either we reach the end of the structure or we find the element E1B1.  I need this loop to be generic, that is, it will work for ANY JDOM XML Document structure, with and number of levels and elements.  This is a brain teaser (at least for me) I am sick of racking my brain on the loop code - I am not a pro Java developer...yet.  Thanks to all who try to answer this question!

EMAIL ME WITH *ANY* QUESTIONS!
Thanks,
Josh
Avatar of jsm11482
jsm11482

ASKER

ONE MORE THING!
When the loop finds the element we're looking for, it should be returned as an element, not a string or integer!
Thanks,
Josh
Avatar of CEHJ
I haven't tried this code, so there might be the odd bug here and there!

  Element findElement(String name,Element startElement){
    if (startElement.getName().equals(name)) return startElement;
    List nodes = startElement.getChildren();
    Iterator iter = nodes.iterator();
    // Recurse if there are any child nodes
    while(iter.hasNext()){
      findElement(name,(Element)iter.next());
    }
  }
Just in case you need to know the imports:
  Element findElement(String name,Element startElement){
    if (startElement.getName().equals(name)) return startElement;
    java.util.List nodes = startElement.getChildren();
    java.util.Iterator iter = nodes.iterator();
    // Recurse if there are any child nodes
    while(iter.hasNext()){
      findElement(name,(Element)iter.next());
    }
  }
Good...i believe this will work fine, how would I make it so I can pass an integer to a function, and it will count through the XML structure until it gets to that element - the nth element? Does that make sense?
-Josh
Hmmm....here is the code I used:

  //Returns the requested element
  public Element getElement(String which,Element startElem)
  {
    List nodes = startElem.getChildren();
    Iterator iter = nodes.iterator();

    if(startElem.getName().equals(which)) return startElem;
    while(iter.hasNext())
    {
      System.out.println(iter.next().toString());
      getElement(which,(Element)iter.next());
    }

    return null;
  }

As you can see I added a println in there to see how many elements it looks through, it only prints out: [Element: <ELEMENT_1/>] which means that it doesnt get past the first element!
Any insight?
Thanks,
-Josh
'return null;'
That's right - I should have included that in my code. Just a preliminary before we go on: tell me, in terms of the document, what node you want. What is it about the node in question that makes it different?
Nothing makes it different, its just nested! And that loop only loops throught he first node for some reason.  Maybe I copied it wrong or something!
-Josh
Just so I know exactly what we're talking about here, can you post the document, or at least some of the parts around the node you're talking about?
hmmm i will put the source code (.java) files as they appear now on my web site:

http://www30.brinkster.com/jsment/CodeSource/Java/XMLParser/XMLParser.java

http://www30.brinkster.com/jsment/CodeSource/Java/XMLParser/XMLtest.java

XMLParser is the document with getElement(String,Element) in it, that is what we are working with! XMLTest is a class that will test XMLParser and XMLString (an in-house XML Parser) and compare execution time and such.  I can not release XMLString as it is one of our software products.  Just comment out any lines that include the word XMLString or reference that somehow, also get rid of the import of com.newsbank.xml.XMLString.

Let me know if anything above does not work!
Thanks,
Josh
OK, I was thinking more of the document itself actually. Are you saying (to use your document example) it's not finding element E1B1?
hmmm i will put the source code (.java) files as they appear now on my web site:

http://www30.brinkster.com/jsment/CodeSource/Java/XMLParser/XMLParser.java

http://www30.brinkster.com/jsment/CodeSource/Java/XMLParser/XMLtest.java

XMLParser is the document with getElement(String,Element) in it, that is what we are working with! XMLTest is a class that will test XMLParser and XMLString (an in-house XML Parser) and compare execution time and such.  I can not release XMLString as it is one of our software products.  Just comment out any lines that include the word XMLString or reference that somehow, also get rid of the import of com.newsbank.xml.XMLString.

Let me know if anything above does not work!
Thanks,
Josh
Yeah it won't find any elements, that is, in the XMLTest class when it says "Finding element...." if calls the getElement function and it always returns a null object! The document is created statically in the XMLTest class in the function textXMLParser.
Thanks,
Josh
OK, I'll have a look at the code
Your site won't let me in there. You can zip and mail if you want to cehjohnson@aol.com.
In the meantime (to use your pseudo-document) I assume findElement("E1",E1") doesn't return null!?
Sorry, assume that the second parameter there is  the variable E1 holding a reference to an E1 element.
Ill send the zip now, and yes, any item i search for returns null!
-josh
Actually, I can see the source all of a sudden!
What we need to do (it might help you to see the problem as well)is to get into the position where I've got compilable code, which means bypassing XMLString for the moment.
This example uses your document example (included at the end in comment):

import javax.xml.parsers.*;
import org.w3c.dom.*;

class FindNode {


     public static Node findNode(Node node, String name) {
          if (node.getNodeName().equals(name)) {
               return node;
          }
          if (node.hasChildNodes()) {
               NodeList list = node.getChildNodes();
               int size = list.getLength();
               for (int i = 0; i < size; i++) {
                    Node found = findNode(list.item(i), name);
                    if (found != null) {
                         return found;
                    }
               }
          }
          return null;
     }


     public static void main(String[] args) {
          buildDom();
     }

     public static void buildDom() {
          DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
          try {
               DocumentBuilder builder = factory.newDocumentBuilder();
               Document document = builder.newDocument();
               Element root = document.createElement("ROOT");
               document.appendChild(root);
               Element E1 = document.createElement("E1");
               root.appendChild(E1);
               Element E1A = document.createElement("E1A");
               E1.appendChild(E1A);
               Element E1B = document.createElement("E1B");
               E1.appendChild(E1B);
               Element E1B1 = document.createElement("E1B1");
               E1B1.appendChild(document.createTextNode("You've found me!"));
               E1B.appendChild(E1B1);
               Node n = findNode(root,"E1B1");
               if (n != null) {
                    System.out.println((n = n.getLastChild()) != null? n.getNodeValue() : "Who stole my text?!");
               }

          } catch (Exception e) {
                e.printStackTrace();
          }
     }

}

/*
<ROOT>
 <E1>
   <E1A>TEXT AND SUCH</E1A>
   <E1B>
     <E1B1>You've found me!</E1B1>
   </E1B>
 </E1>
</ROOT>
*/
So we could adapt that to work with JDOM then?
-Josh
Yes, it could. I did it in JDK classes as I've not got JDOM installed, but since I was thinking of getting it anyway, I may send you an adapted version later.
Here it is as a utility class adapted for JDOM:

import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.jdom.input.DOMBuilder;
import org.jdom.Element;
import java.util.Iterator;
import java.util.List;

class FindNode {

     public static Node findNode(Node node, String name) {
          if (node.getNodeName().equals(name)) {
               return node;
          }
          if (node.hasChildNodes()) {
               NodeList list = node.getChildNodes();
               int size = list.getLength();
               for (int i = 0; i < size; i++) {
                    Node found = findNode(list.item(i), name);
                    if (found != null) {
                         return found;
                    }
               }
          }
          return null;
     }

     public static void main(String[] args) {
          buildDom();
     }

     public static void buildDom() {
          DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
          try {
               DocumentBuilder builder = factory.newDocumentBuilder();
               Document document = builder.newDocument();
               org.w3c.dom.Element root = document.createElement("ROOT");
               document.appendChild(root);
               org.w3c.dom.Element E1 = document.createElement("E1");
               root.appendChild(E1);
               org.w3c.dom.Element E1A = document.createElement("E1A");
               E1.appendChild(E1A);
               org.w3c.dom.Element E1B = document.createElement("E1B");
               E1.appendChild(E1B);
               org.w3c.dom.Element E1B1 = document.createElement("E1B1");
               E1B1.appendChild(document.createTextNode("You've found me!"));
               E1B.appendChild(E1B1);

               // Now create a JDOM document to test the finder
               DOMBuilder jdomBuilder = new DOMBuilder();
               org.jdom.Document doc = jdomBuilder.build(document);
               org.jdom.Element e = DomUtils.getElement("E1B1", doc.getRootElement());
               String s = null;
               System.out.println(e != null && (s = e.getText()) != null ? s : "Who stole my element/text?!");
               
          } catch (Exception e) {
               e.printStackTrace();
          }
     }


}

/**
 * Various utilties for manipulating XML DOMs
 *
 * @author     Josh
 * @created    03 July 2002
 */
class DomUtils {


     /**
      * Gets a child element given a starting parent element
      *
      * @param  which      The name of the element to find
      * @param  startElem  The starting element
      * @return            The element found
      */
     public static Element getElement(String which, org.jdom.Element startElem) {
          if (startElem.getName().equals(which)) {
               return startElem;
          }
          if (startElem.hasChildren()) {
               List nodes = startElem.getChildren();
               Iterator iter = nodes.iterator();
               while (iter.hasNext()) {
                    org.jdom.Element found = getElement(which, (org.jdom.Element) iter.next());
                    if (found != null) {
                         return found;
                    }
               }
          }
          return null;
     }

}

/*
 *  <ROOT>
 *  <E1>
 *  <E1A>TEXT AND SUCH</E1A>
 *  <E1B>
 *  <E1B1>You've found me!</E1B1>
 *  </E1B>
 *  </E1>
 *  </ROOT>
 */
Great! Now, I am new to Java, would it be too much trouble for me to ask you to change that XMLParser.java file that I emailed you and put your code in there because I am not exactly sure how it works!
You already won the points, obviously, so you don't have to do this, just let me know!
Thanks,
Josh
ASKER CERTIFIED SOLUTION
Avatar of CEHJ
CEHJ
Flag of United Kingdom of Great Britain and Northern Ireland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thanks for all of the time and effort, I will work on implementing your ideas! Here are your points!
Thanks,
Josh