A Java object tree implementation based on the Composite Pattern combined with a tolerant XML/JSON reader for reading and writing any Extensible Markup Language (XML) or JavaScript Object Notation (JSON) structure - no need for an XML Schema Definition XSD.
███████████ █████████ █████ █████
▒▒███▒▒▒▒▒███ ███▒▒▒▒▒███ ▒▒███ ▒▒███
▒███ ▒███ ▒███ ▒███ ▒▒███ ███
▒██████████ ▒███████████ ▒▒█████
▒███▒▒▒▒▒▒ ▒███▒▒▒▒▒███ ███▒███
▒███ ▒███ ▒███ ███ ▒▒███
█████ █████ █████ █████ █████
▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒
Each created object can be stored as a node in a tree. There are no special types. Any object can start a fresh tree by being root. A root can store children. Those can be added or set. Any children can be fetched by tag or in case of many with the same name, by listing and filtering. Each object can also store attributes. Those are organized in the same way as children but are used as a list. By any object, the tree can be recursed and generated to any data format. Both XML and JSON formats are supported.
- XML Read/Write - SAX-based parser for reading XML, generator for writing indented or compact XML
- JSON Read/Write - Parse and generate JSON using the PAX JSON format
- XPath-like Search - Navigate trees with absolute (
/root/child/grandchild) and relative (./child/sibling) paths - Deep Copy - Create independent copies of entire subtrees
- No Schema Required - Tolerant reader parses any well-formed XML/JSON without needing an XSD
- Lazy Initialization - Children and attributes collections are created on-demand for memory efficiency
- Duplicate Handling - Multiple siblings with the same tag are supported via synthetic keys
- Java 21 or higher
- Gradle 9.x (wrapper included)
./gradlew build jarThis creates the JAR file in build/libs/.
// Create a simple node tree
IPax root = Instances.Factory().produce("library");
root.Attrib().add("name", "My Library");
// Add children
IPax book = Instances.Factory().produce("book");
book.Child().add("title", "Effective Java");
root.Child().add(book);
// Generate XML
String xml = root.XML();
System.out.println(xml);
// Generate JSON
String json = root.JSON();
System.out.println(json);Output:
<library name="My Library">
<book>
<title>Effective Java</title>
</book>
</library>{"__tag__": "library", "__attributes__": [{"name": "name", "value": "My Library"}], "__children__": {"book": {"__tag__": "book", "__children__": {"title": {"__tag__": "title", "__value__": "Effective Java"}}}}}Get the factory to create new nodes:
IFactory factory = Instances.Factory();Create a new node with a tag name:
IPax root = Instances.Factory().produce("root");Create a new node with a tag and value:
IPax element = Instances.Factory().produce("title", "Effective Java");Create a deep copy of an existing node:
IPax original = Instances.Factory().produce("book");
IPax copy = Instances.Factory().copy(original);All node operations are available through the IPax interface.
IPax root = Instances.Factory().produce("book");
String tag = root.Tag(); // returns "book"IPax root = Instances.Factory().produce("root");
root.Tag("book"); // changes tag to "book"IPax node = Instances.Factory().produce("element");
boolean hasTag = node.hasTag(); // true
IPax noTag = Instances.Factory().produce(null);
boolean empty = noTag.hasTag(); // falseIPax title = Instances.Factory().produce("title", "My Book");
String value = title.Val(); // returns "My Book"IPax element = Instances.Factory().produce("description");
element.Val("A great book");IPax element = Instances.Factory().produce("empty");
boolean hasValue = element.hasVal(); // false
element.Val("content");
hasValue = element.hasVal(); // trueNote: Empty, blank, or newline-only values are rejected and stored as null.
IPax parent = Instances.Factory().produce("parent");
IPax child = Instances.Factory().produce("child");
parent.Child().add(child);
IPax foundParent = child.Parent(); // returns parentIPax parent = Instances.Factory().produce("parent");
IPax child = Instances.Factory().produce("child");
child.Parent(parent);IPax root = Instances.Factory().produce("root");
boolean hasParent = root.hasParent(); // false
IPax child = Instances.Factory().produce("child");
root.Child().add(child);
hasParent = child.hasParent(); // trueIPax root = Instances.Factory().produce("library");
IPax book = Instances.Factory().produce("book");
root.Child().add(book);
IPax chapter = Instances.Factory().produce("chapter");
book.Child().add(chapter);
String path = chapter.Path(); // returns "/library/book/chapter"IChildren children = root.Child();boolean has = root.hasChild();IAttributes attributes = root.Attrib();boolean has = root.hasAttrib();IPax root = Instances.Factory().produce("book");
root.Attrib().add("id", "1");
IPax title = Instances.Factory().produce("title", "Effective Java");
root.Child().add(title);
String xml = root.XML();
/*
<book id="1">
<title>Effective Java</title>
</book>
*/String xml = root.XML_lined(); // <book id="1"><title>Effective Java</title></book>IPax root = Instances.Factory().produce("book");
root.Attrib().add("id", "1");
IPax title = Instances.Factory().produce("title", "Effective Java");
root.Child().add(title);
String json = root.JSON();
/*
{"__tag__": "book", "__attributes__": [{"name": "id", "value": "1"}],
"__children__": {"title": {"__tag__": "title", "__value__": "Effective Java"}}}
*/Manage child nodes.
root.Child().add("chapter");root.Child().add("author", "Joshua Bloch");IPax chapter = Instances.Factory().produce("chapter", "Introduction");
root.Child().add(chapter);IPax chapter = root.Child().get("chapter");IPax first = root.Child().get(0);boolean exists = root.Child().has("chapter");IPax first = root.Child().First();List<IPax> allChildren = root.Child().all();List<IPax> chapters = root.Child().all("chapter");int count = root.Child().cnt();root.Child().del("chapter");root.Child().del(chapter);root.Child().del();root.Child().set("title", "New Title");root.Child().set(newChapter);IPax found = root.Child().search("/library/book/chapter");
IPax foundRelative = root.Child().search("./book/chapter");// Find all <author> nodes anywhere in the tree
List<IPax> allAuthors = root.Child().searchByTag("author");// Find all <chapter> nodes under <book> under <library>
List<IPax> chapters = root.Child().searchByPath("/library/book/chapter");List<Book> books = root.Child().typed(); // assuming all children are Book instancesList<Book> books = root.Child().typed("book");Manage attributes on a node.
root.Attrib().add("id", "123");
root.Attrib().add("type", "novel");IPax attr = Instances.Factory().produce("class", "primary");
root.Attrib().add(attr);IPax id = root.Attrib().get("id");
String value = id.Val(); // "123"boolean hasId = root.Attrib().has("id");List<IPax> attrs = root.Attrib().all();int count = root.Attrib().cnt();root.Attrib().del("id");root.Attrib().del();String attrXml = root.Attrib().XML(); // returns "id=\"123\" type=\"novel\" "Parse XML from various sources.
XmlReader reader = XmlReader.Instance;IPax root = XmlReader.Instance.parse("./config.xml");IPax root = XmlReader.Instance.parseLocalFile("data.xml");String xml = "<book><title>Java</title></book>";
InputStream is = new ByteArrayInputStream(xml.getBytes());
IPax root = XmlReader.Instance.stream(is);Write IPax trees to files.
XmlWriter writer = XmlWriter.Instance;IPax book = Instances.Factory().produce("book");
book.Val("Content");
boolean success = XmlWriter.Instance.XML(book); // writes to "book.xml"boolean success = XmlWriter.Instance.XML(book, "mybook.xml");
boolean success2 = XmlWriter.Instance.XML(book, "output"); // adds .xml automaticallyParse JSON from various sources. Uses the PAX JSON format.
JsonReader reader = JsonReader.Instance;IPax root = JsonReader.Instance.parse("./data.json");String json = "{\"__tag__\": \"book\", \"__value__\": \"Content\"}";
InputStream is = new ByteArrayInputStream(json.getBytes());
IPax root = JsonReader.Instance.stream(is);String json = "{\"__tag__\": \"library\", \"__children__\": {\"book\": {\"__tag__\": \"book\"}}}";
IPax root = JsonReader.Instance.parseJson(json);The PAX JSON format uses four reserved keys:
__tag__- the XML tag name__value__- the text content, if present__attributes__- an array of{name, value}objects__children__- a map of child tag to child object or array
When multiple sibling children share the same tag they are grouped into a JSON array.
String json = root.JSON();// Create root
IPax library = Instances.Factory().produce("library");
// Add attributes
library.Attrib().add("name", "City Library");
library.Attrib().add("location", "Downtown");
// Add children
IPax book1 = Instances.Factory().produce("book");
book1.Attrib().add("id", "1");
book1.Child().add("title", "Effective Java");
book1.Child().add("author", "Joshua Bloch");
library.Child().add(book1);
IPax book2 = Instances.Factory().produce("book");
book2.Attrib().add("id", "2");
book2.Child().add("title", "Clean Code");
book2.Child().add("author", "Robert Martin");
library.Child().add(book2);
// Generate XML
String xml = library.XML();
System.out.println(xml);Output:
<library name="City Library" location="Downtown">
<book id="1">
<title>Effective Java</title>
<author>Joshua Bloch</author>
</book>
<book id="2">
<title>Clean Code</title>
<author>Robert Martin</author>
</book>
</library>// Read from file
IPax library = XmlReader.Instance.parse("library.xml");
// Find specific book using search
IPax book = library.Child().search("/library/book");
// Add new child
IPax review = Instances.Factory().produce("review", "Excellent!");
book.Child().add(review);
// Write back to file
XmlWriter.Instance.XML(library, "library_updated.xml");IPax original = Instances.Factory().produce("book");
original.Attrib().add("id", "1");
original.Child().add("title", "Original");
IPax copy = Instances.Factory().copy(original);
copy.Attrib().add("id", "2");
copy.Child().get("title").Val("Copy");
// Both nodes exist independentlyimport java.io.FileWriter;
// Create a tree
IPax root = Instances.Factory().produce("library");
root.Attrib().add("name", "My Library");
IPax book = Instances.Factory().produce("book");
book.Attrib().add("id", "1");
book.Child().add("title", "Effective Java");
root.Child().add(book);
// Generate JSON
String json = root.JSON();
System.out.println(json);
// Write JSON to file using standard Java I/O
try (FileWriter writer = new FileWriter("library.json")) {
writer.write(json);
}
// Read JSON back from file
IPax loaded = JsonReader.Instance.parse("library.json");
// Verify round-trip
String xml = loaded.XML();
System.out.println(xml);./gradlew build./gradlew jar./gradlew test./gradlew clean buildThe JAR file will be created in build/libs/.
Note: The Gradle wrapper is version 9. If you need a different version, see the Gradle installation guide.
MIT License - See LICENSE file