EAXY is a fluent Java library for building, parsing, searching and manipulating XML. It aims to have a compact syntax for doing the most common operations on XML and HTML trees. It is designed to be pleasant to use, both when you work with simple and small XML files and when you work with huge, poorly structured or deeply nested XML files where code generation is not feasible.
It's very suitable for building XML structures to use in unit tests.
Eaxy is small (< 100 kB) and has no dependencies.
EAXY is build with the following guiding principles:
- Namespaces must be build in from the start
- Links to parent nodes and document complicates the design and imposes problems with copying subtres
- Libraries should prefer reimplementing trivial code over adding dependencies
- Parse large XML documents (a 90 MB document in 3 seconds!):
for (Element subtree : Xml.filter("html", "body", "ul", "li").iterate(reader))
- Build complex XML documents with a simple internal DSL
Xml.el("html", Xml.el("body", Xml.el("h4", "A message")))
- Flexibly traverse complex XML documents
doc.find("body", "h4").check().text(); doc.find("body", "ul", "li").texts()
- Experimental: Random XML services for testing
SoapSimulatorServer server = new SoapSimulatorServer(10080); server.addSoapEndpoint(...); server.start();
Namespace A_NS = new Namespace("uri:a", "a");
Namespace B_NS = new Namespace("uri:b", "b");
Element xml = A_NS.el("root",
Xml.el("node-without-namespace", "element text"),
A_NS.el("parent",
A_NS.el("child")),
B_NS.el("other-parent", Xml.text("Here's some text")));
Element div = el("div");
assertThat(div.className()).isNull();
div.addClass("important");
div.addClass("hidden");
assertThat(div.className()).isEqualTo("important hidden");
div.removeClass("hidden");
div.removeClass("hidden");
assertThat(div.className()).isEqualTo("important");
div.removeClass("important");
assertThat(div.className()).isEmpty();
Element xml = el("div",
el("div").id("not-here").add(text("something")),
el("div").id("below-here").add(
el("div", el("div", el("p", text("around "), el("span", "HERE"), text(" around"))))));
assertThat(xml.find("...", "#below-here", "...", "p", "...").first().text())
.isEqualTo("HERE");
ElementQuery filter = Xml.filter("parentElement", "childElement");
try (Reader reader = new FileReader(hugeFile)) {
for (Element element : filter.iterate(reader)) {
if ("active".equals(element.find("status").first().attr("value"))) {
System.out.println(element.find("description", "name").first().text());
}
}
}
Element form = el("form",
el("input").id("first_name_id").name("first_name").type("text").val("Darth"),
el("input").id("last_name_id").name("last_name").type("text").val("Vader"),
el("input").name("createPerson").type("submit").val("Create person"));
assertThat(form.find("input").ids()).containsExactly("first_name_id", "last_name_id");
assertThat(form.find("input").values()).containsExactly("Darth", "Vader", "Create person");
assertThat(form.find("input").names()).containsExactly("first_name", "last_name", "createPerson");
assertThat(form.find("input").first().val()).isEqualTo("Darth");
SampleSoapXmlBuilder builder = new SampleSoapXmlBuilder("xsd/StockQuoteService.wsdl");
Element output = builder
.service("StockQuoteService")
.operation("GetLastTradePrice")
.randomOutput("m");
assertThat(output.tagName()).isEqualTo("TradePrice");
double price = Float.parseFloat(output.find("price").single().text());
SampleXmlBuilder generator = new SampleXmlBuilder(getClass().getResource("/xsd/po.xsd"), "po");
generator.setFull(true);
Element el = generator.createRandomElement("purchaseOrder");
assertThat(el.hasAttr("orderDate")).isTrue();
assertThat(el.find("comment")).isNotEmpty();
assertThat(el.find("items").check().find("item")).isNotEmpty();
assertThat(el.find("shipTo").single().attr("country")).isEqualTo("US");
generator.getValidator().validate(el);
- SAX namespace may be broken with certain classpath combinations - perhaps I need to write my own parser
- Finding doesn't include the current element, which is often non-intuitive
- The "..." syntax is unintuitive and could use some improvement