Is this the right way to use javax.xml.xpath?

Discussion in 'Java' started by Larry Coon, Jun 5, 2006.

  1. Larry Coon

    Larry Coon Guest

    I'm trying to figure out the right way to use the xpath api
    under 1.5. I'm not sure I'm doing it the right way when a
    node has multiple children -- I can get to the data, but my
    technique seems kludgy. The samples I found online tell you
    how to get a NodeList, but don't really help you from there.

    First, here is a sample document (XPathTest.xml):

    <?xml version="1.0" encoding="utf-8"?>

    <test>
    <one>This is a single item</one>

    <many>
    <item>This is one of many items #1</item>
    <item>This is one of many items #2</item>
    <item>This is one of many items #3</item>
    </many>
    </test>

    -----

    Here's an SSCCE. The technique I use to get the single item
    is easy enough, but I start to scratch my head when I go after
    the many items:

    import java.io.*;
    import javax.xml.parsers.*;
    import javax.xml.xpath.*;
    import org.w3c.dom.*;

    public class TestXPath {
    public TestXPath(String configFileName) {
    try {
    DocumentBuilderFactory domFactory =
    DocumentBuilderFactory.newInstance();

    DocumentBuilder builder = domFactory.newDocumentBuilder();
    Document document = builder.parse(new File(configFileName));

    XPathFactory xFactory = XPathFactory.newInstance();
    XPath xPath = xFactory.newXPath();

    String s1 = xPath.evaluate("/test/one", document);
    System.out.println("One item: " + s1);

    NodeList nodes = (NodeList)
    xPath.evaluate("/test/many/item",
    document, XPathConstants.NODESET);

    int length = nodes.getLength();
    System.out.println("Number of items: " + length);

    for (int i = 0; i < length; i++) {
    String s2 = xPath.evaluate("/test/many/item["
    + (i + 1) + "]", document);

    System.out.println("Many items #" + i + ": " + s2);
    }
    }
    catch (Exception x) { x.printStackTrace(); }
    }

    public static void main(String[] args) {
    new TestXPath("XPathTest.xml");
    }
    }

    -----

    I realize that a NodeList isn't Iterable, so I need to use a for loop
    to get the individual items. But the only thing I'm using the NodeList
    for is to get a count -- I go back to the DOM to retrieve the actual
    data. This doesn't seem right. Should I be using XPath on the NodeList
    to get at the data more directly? I tried various ways, and didn't get
    anything to work. Any advice?
    Larry Coon, Jun 5, 2006
    #1
    1. Advertising

  2. Larry Coon

    Chris Smith Guest

    Larry Coon <> wrote:
    > I'm trying to figure out the right way to use the xpath api
    > under 1.5. I'm not sure I'm doing it the right way when a
    > node has multiple children -- I can get to the data, but my
    > technique seems kludgy. The samples I found online tell you
    > how to get a NodeList, but don't really help you from there.


    Once you've got a NodeList, you would typically fall back to the normal
    DOM API to extract the data from the node. If you need (or just want)
    to perform further XPath queries, you can pass the Node from the
    NodeList as context. For example...

    public class TestXPath
    {
    public static void main(String[] args) throws Exception
    {
    DocumentBuilderFactory f = DocumentBuilderFactory.newInstance();
    DocumentBuilder b = f.newDocumentBuilder();
    Document d = b.parse(new InputSource(new ByteArrayInputStream(
    "<test><many><item>a</item><item>b</item></many></test>"
    .getBytes("US-ASCII"))));

    XPathFactory xf = XPathFactory.newInstance();
    XPath xp = xf.newXPath();

    NodeList nl = (NodeList) xp.evaluate(
    "/test/many/item", d, XPathConstants.NODESET);

    for (int i = 0; i < nl.getLength(); i++)
    {
    Node n = nl.item(i);
    System.out.println(xp.evaluate(".", n));
    }
    }
    }

    --
    Chris Smith - Lead Software Developer / Technical Trainer
    MindIQ Corporation
    Chris Smith, Jun 5, 2006
    #2
    1. Advertising

  3. Larry Coon

    Larry Coon Guest

    Chris Smith wrote:

    > Once you've got a NodeList, you would typically fall back to the normal
    > DOM API to extract the data from the node.


    Thanks for the reply, Chris. This is one of the things that made
    me want to ask -- falling back on the DOM API once I got to a certain
    point seemed really inelegant. I assumed it _couldn't_ be the right
    way to do it.

    > If you need (or just want)
    > to perform further XPath queries, you can pass the Node from the
    > NodeList as context. For example...


    (example snipped)

    Ah, I tried various ways of doing evalutate() on the node, but I
    never used the expression ".". I guess that warrants a "duh."

    What I'd really like to do is eliminate the iteration altogether.
    The contents are being used to populate a Collection, so I'd love
    to be able to just make one call to addAll(). Is there a set-based
    XPath operation that I can use as the argument to addAll()?
    Larry Coon, Jun 5, 2006
    #3
  4. Larry Coon

    Chris Smith Guest

    Larry Coon <> wrote:
    > What I'd really like to do is eliminate the iteration altogether.
    > The contents are being used to populate a Collection, so I'd love
    > to be able to just make one call to addAll(). Is there a set-based
    > XPath operation that I can use as the argument to addAll()?


    Not that I can see. However, you can write the following once, in a
    utility lib somewhere:

    public class Nodes
    {
    public static List<Node> asList(final NodeList nl)
    {
    return new AbstractList<Node>() {
    @Override
    public int size()
    {
    return nl.getLength();
    }

    @Override
    public Node get(int index)
    {
    return nl.item(index);
    }
    };
    }
    }

    And,

    col.addAll(Nodes.asList(xpath.evaluate(...),
    XPathConstants.NODESET)));

    Is that close to what you want? Feel free to drop the @Override and
    <Node> from the code above if you aren't using Java 5.0 for the project.

    --
    Chris Smith - Lead Software Developer / Technical Trainer
    MindIQ Corporation
    Chris Smith, Jun 5, 2006
    #4
  5. Larry Coon

    Larry Coon Guest

    Chris Smith wrote:

    > Not that I can see. However, you can write the following once, in a
    > utility lib somewhere:


    (snipped)

    Ah, that makes perfect sense -- thanks!
    Larry Coon, Jun 5, 2006
    #5
  6. Larry Coon

    Larry Coon Guest

    Chris Smith wrote:

    > Not that I can see. However, you can write the following once, in a
    > utility lib somewhere:
    >
    > public class Nodes
    > {
    > public static List<Node> asList(final NodeList nl)
    > {
    > return new AbstractList<Node>() {
    > @Override
    > public int size()
    > {
    > return nl.getLength();
    > }
    >
    > @Override
    > public Node get(int index)
    > {
    > return nl.item(index);
    > }
    > };
    > }
    > }
    >
    > And,
    >
    > col.addAll(Nodes.asList(xpath.evaluate(...),
    > XPathConstants.NODESET)));
    >
    > Is that close to what you want?


    On second look, it isn't quite what I want. I don't want to populate
    a collection of Nodes, I want to populate a collection of Strings. I
    suppose the approach is to send the XPath to asList(), so get() can use
    it to evaluate() the node and return the resulting String?
    Larry Coon, Jun 5, 2006
    #6
  7. Larry Coon

    Chris Smith Guest

    Larry Coon <> wrote:
    > On second look, it isn't quite what I want. I don't want to populate
    > a collection of Nodes, I want to populate a collection of Strings. I
    > suppose the approach is to send the XPath to asList(), so get() can use
    > it to evaluate() the node and return the resulting String?


    Sure, that'll work.

    Let me clarify something: I used the XPath instance to extract the text
    from a node in my first reply only because it is more general. I
    assumed that in practice you may want to perform more operations to
    which XPath is well-suited. XPath is a way to find your way around an
    XML file; it's not a replacement for DOM. If all you want is the text
    of a node, just call getTextContent() on the Node, and you're done.
    XPath is good for navigation, which is clumsy and painful in the DOM,
    but XPath is clumsy and painful for direct operations on a single node
    of an XML document.

    In a less trivial example where XPath is really appropriate, it would
    perhaps still be cleaner for the implementation of Nodes.asList to
    construct its own XPath instance, since there's no reason to assume that
    the original NodeList comes from XPath code and therefore no reason to
    assume that the caller has an XPath instance to supply.

    Also, you'll probably want to rename my Nodes.asList to something else,
    since it no longer does what the name suggests.

    If you again want to be somewhat general, you can write:

    public static interface Filter<S,T>
    {
    public T exec(S arg);
    }

    public static <S,T> List<T> mapList(List<S> source, Filter<S,T> f)
    {
    List<T> r = new ArrayList<T>(source.size());
    for (S from : source) r.add(f.exec(from));
    return r;
    }

    and then,

    col.addAll(MyUtil.mapList(
    Nodes.asList(xpath.evaluate(...)),
    new Filter<Node,String>() {
    public String exec(Node arg)
    {
    return arg.getTextContent();
    }
    }));

    --
    Chris Smith - Lead Software Developer / Technical Trainer
    MindIQ Corporation
    Chris Smith, Jun 5, 2006
    #7
  8. Larry Coon

    Larry Coon Guest

    Chris Smith wrote:

    > Sure, that'll work.
    >
    > Let me clarify something:


    (clarification snipped)

    Thanks Chris, I appreciate all the help.


    Larry Coon
    University of California
    Larry Coon, Jun 13, 2006
    #8
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Replies:
    1
    Views:
    739
    Esmond Pitt
    Mar 27, 2005
  2. Marvin_123456

    "Memory leak" in javax.xml.xpath.XPath

    Marvin_123456, Jul 29, 2005, in forum: Java
    Replies:
    4
    Views:
    1,964
    jan V
    Jul 29, 2005
  3. lizard
    Replies:
    0
    Views:
    1,761
    lizard
    Jan 30, 2006
  4. Andrew Thompson

    javax.servlet and javax.servlet.http

    Andrew Thompson, Apr 24, 2007, in forum: Java
    Replies:
    1
    Views:
    650
    newbie_at_tomcat
    Apr 25, 2007
  5. Lew
    Replies:
    1
    Views:
    609
    newbie_at_tomcat
    Apr 25, 2007
Loading...

Share This Page