[Date Prev]
| [Thread Prev]
| [Thread Next]
| [Date Next]
--
[Date Index]
| [Thread Index]
An XML API using Java streams
- From: Michael Kay <mike@saxonica.com>
- To: XML Developers List <xml-dev@lists.xml.org>
- Date: Tue, 20 Jun 2017 09:51:08 +0100
I'm struck how 95% of Java/XML users still seem to be stuck on DOM navigation, perhaps with a bit of XPath thrown in, which all seems a terribly late-20th-century way of doing things. The success of jQuery in the Javascript world, and the emergence of the Java Streams API, suggests there ought to be a better way. I don't know if anyone has done this before, but I thought I'd sketch out some ideas. (I have to say me experience with the Streams API is pretty cursory, to coin a phrase, so there may well be better ways of doing things that I've missed.
In particular:
(a) It would be nice to navigate using functional stream-based interfaces rather than procedural step-by-step navigation
(b) It would be nice to do all the navigation using Java primitives without requiring XPath as a separate interpreted sub-language.
Navigation is basically a question of constructing streams of nodes. Let's start by defining the XPath axes as functions (Node -> Stream(Node)). For example, given a node N, Axis.child(N) returns a stream containing the children of N.
If we define Node.walk(F) as returning a stream that navigates using the supplied function F, then
N.walk(Axis::child)
gets the children of N (you could write Axis.child(N) if you prefer), while
N.walk(Axis::child).flatMap(Axis::child)
gets the grandchildren.
If we define NodeTest::isElement as a function that returns true if the argument is an element node, then
N.walk(Axis::child).filter(NodeTest::isElement)
returns a stream of child nodes that are elements.
If we want to match nodes with a specific local name, then NodeTest.withLocalName(String) returns a function that returns true for nodes having that local name, so we can write
N.walk(Axis::child).filter(NodeTest.withLocalName("city"))
This is getting a bit long-winded for a simple task, so perhaps Axis.child(N) should return a function that navigates the child axis and filters by name, so the above becomes
N.walk(Axis.child("city"))
If Node.stringValue() returns the string value of a node
N.walk(Axis.child("city")).flatMap(Axis.attribute("name").map(Node::stringValue)
returns the names of the child cities (as a Stream<String>).
Perhaps there's a shortcut for this:
N.walk(Axis.child("city")).map(Axis.attValue("name"))
And then we could add tree construction using function chaining, e.g.
element("cities")
.addChild(element("city").addAttribute("name", "Rome"))
.addChild(element("city").addAttribute("name", "Paris"))
=====
Just an initial sketch of some ideas. Does it seem worth pursuing?
Michael Kay
Saxonica
[Date Prev]
| [Thread Prev]
| [Thread Next]
| [Date Next]
--
[Date Index]
| [Thread Index]