Uniform API to read/write tree data models β cross-format, cross-library.
tree-io provides a consistent abstraction for working with tree-like data structures. It is format-agnostic (JSON, YAML, CBOR, β¦) and library-agnostic (Jackson, Gson, Jakarta, β¦), allowing you to read, manipulate, and write trees uniformly without depending on a specific parser or serializer.
- π Uniform, library-agnostic API for tree data processing
- ποΈ Supports multiple formats: JSON, YAML, CBOR
- π Works with Jackson, Gson, Jakarta, and other libraries
- π οΈ Extensible adapter model for adding new formats or libraries
- Uniform processing of tree-structured data
- Building library-agnostic processors and pipelines
- Manipulating hierarchical data in a consistent way
Artifact | Version | Javadoc |
---|---|---|
Tree I/O API | ||
Jakarta JSON API | ||
Jackson 2 Tree Model | ||
CBOR |
The most common use case is a full transformation from a source to a destination.
// Have a source object (e.g., a Map) and a destination (e.g., a JsonGenerator)
Map<String, Object> source = Map.of("hello", "world");
JsonGenerator destination = ... ;
// Create an adapter for the source and a writer for the destination
var adapter = new NativeAdapter();
var jsonWriter = new Jackson2Writer(destination);
var cborWriter = new CborWriter(destination);
// Run the transformation with a single call
jsonWriter.node(source, adapter);
// or/and
cborWriter.node(source, adapter);
Use a NodeAdapter directly when you need to read, inspect, or extract specific values from a tree structure without traversing the entire tree.
// Given any 'node' object and a suitable 'adapter'...
// To inspect and process it safely:
// 1. Check for structural types first.
if (adapter.isMap(node)) {
// If it's a map, iterate its entries.
for (Entry<?, ?> entry : adapter.entries(node)) {
// ... process key and value recursively
}
} else if (adapter.isCollection(node)) {
// If it's a collection, iterate its elements.
for (Object element : adapter.elements(node)) {
// ... process element recursively
}
// 2. Then, check for scalar types.
} else if (adapter.isString(node)) {
String value = adapter.stringValue(node);
} else if (adapter.isNumber(node)) {
// For numbers, check for the specific type before extracting.
if (adapter.isIntegral(node)) {
long intValue = adapter.longValue(node);
} else {
BigDecimal decimalValue = adapter.decimalValue(node);
}
} else if (adapter.isNull(node)) {
// It's a null value.
}
For complex processing like searching or validation, you can manually iterate through the tree using the step() method of a NodeVisitor.
// Given a source object and a suitable adapter...
Object source = ... ;
NodeAdapter adapter = ... ;
// 1. Create the visitor instance.
NodeVisitor visitor = NodeVisitor.of(source, adapter);
// 2. Loop step-by-step through every node in the tree.
while (visitor.step()) {
// 3. Inspect the visitor's state after each step.
Object node = visitor.currentNode();
NodeType type = visitor.currentNodeType();
Context context = visitor.currentNodeContext();
// 4. Perform actions based on the node's role in the tree.
switch (context) {
case ROOT:
// Process the top-level node.
break;
case PROPERTY_KEY:
// The current node is a key in a map.
// The next step() will move to its value.
break;
case PROPERTY_VALUE:
// The current node is a value in a map.
break;
case COLLECTION_ELEMENT:
// The current node is an element in a collection.
break;
case END:
// A synthetic marker showing a map or collection has ended.
break;
}
}
<!-- Core API: uniform interfaces implemented by all adapters -->
<dependency>
<groupId>com.apicatalog</groupId>
<artifactId>tree-io-api</artifactId>
<version>${tree-io.version}</version>
</dependency>
<!-- Available adapters: include the one(s) matching your use case -->
<!-- Jakarta JSON API adapter -->
<dependency>
<groupId>com.apicatalog</groupId>
<artifactId>tree-io-jakarta</artifactId>
<version>${tree-io.version}</version>
</dependency>
<!-- Jackson 2 Tree Model adapter -->
<dependency>
<groupId>com.apicatalog</groupId>
<artifactId>tree-io-jackson2</artifactId>
<version>${tree-io.version}</version>
</dependency>
<!-- CBOR adapter -->
<dependency>
<groupId>com.apicatalog</groupId>
<artifactId>tree-io-cbor</artifactId>
<version>${tree-io.version}</version>
</dependency>
Contributions are welcome! Please submit a pull request.
Fork and clone the repository, then build with Maven:
> cd tree-io
> mvn package
Commercial support and consulting are available.
For inquiries, please contact: filip26@gmail.com