At its simplest, Optimus is just a wrapper around a SAX reader and XML writer - which will, given no more instruction, just perform an "identity transform". For example:-
String inputXml = "<root><foo><bar>text node</bar></foo></root>";
Optimus transformer = new Optimus(inputXml);
String outputXml = transformer.transform();
assertEquals(inputXml, outputXml);
Doing the same, but instead writing the output to a file...
String inputXml = "<root><foo><bar>text node</bar></foo></root>";
Optimus transformer = new Optimus(testXml);
transformer.setOmitXmlDeclaration(false); // we want an xml decl in files!
Writer writer = new OutputStreamWriter(new FileOutputStream("C:/temp/out.xml"), "UTF-8");
transformer.transform(writer);
Or reading from an input XML file and writing to another...
Reader reader = new InputStreamReader(new FileInputStream("C:/temp/in.xml"));
Optimus transformer = new Optimus(reader);
transformer.setOmitXmlDeclaration(false); // we want an xml decl in files!
Writer writer = new OutputStreamWriter(new FileOutputStream("C:/temp/out.xml"), "UTF-8");
transformer.transform(writer);
Of course, a straight identity transform isn't much use - we generally want to make some changes. Optimus provides the ability to register handlers that can alter the output. For example, say we wanted to rename all the <foo>
elements to <baz>
...
Optimus transformer = new Optimus("<root><foo><bar>text node</bar></foo></root>");
transformer.registerStartElementHandler("foo", (context, cargo, writer) -> {
context.setOverrideName(new QName("baz"));
return null;
});
String outputXml = transformer.transform();
Or say we wanted to remove the <foo>
elements (but keep the nodes within them)...
Optimus transformer = new Optimus("<root><foo><bar>text node</bar></foo></root>");
transformer.registerStartElementHandler("foo", (context, cargo, writer) -> {
return ContinueState.SKIP_THIS;
});
String outputXml = transformer.transform();
Or to remove the <foo>
elements altogether (including their descendant nodes)...
Optimus transformer = new Optimus("<root><foo><bar>text node</bar></foo></root>");
transformer.registerStartElementHandler("foo", (context, cargo, writer) -> {
return ContinueState.SKIP_THIS_AND_DESCENDANTS;
});
String outputXml = transformer.transform();
Or perhaps wrapping the <foo>
elements with a <baz>
element around them...
Optimus transformer = new Optimus("<root><foo><bar>text node</bar></foo></root>");
transformer.registerStartElementHandler("foo", (context, cargo, writer) -> {
writer.writeStartElement("baz"); // wrap before <foo> gets written
return null;
});
transformer.registerEndElementHandler("foo", (context, cargo, writer) -> {
writer.writeEndElement(); // end the <foo>
writer.writeEndElement(); // end our new <baz> element
// tell transformer that we've done what needs to be done...
return ContinueState.HANDLED;
});
String outputXml = transformer.transform();
Or wrapping all the descendants of the <foo>
elements with a <baz>
element...
Optimus transformer = new Optimus("<root><foo><bar>text node</bar></foo></root>");
transformer.registerAfterStartElementHandler("foo", (context, cargo, writer) -> {
writer.writeStartElement("baz"); // wrap start before <foo> gets written
return null;
});
transformer.registerEndElementHandler("foo", (context, cargo, writer) -> {
writer.writeEndElement(); // end our new <baz> element
return ContinueState.CONTINUE; // let transformer end the <foo> element as normal
});
String outputXml = transformer.transform();
The cargo passed to each handler can be used to track a state during transform. For example, say we wanted to add an @id
attribute with a sequential value to each and every element...
class CounterHolder {
Long counter = 0L;
}
Optimus<CounterHolder> transformer = new Optimus<CounterHolder>("<root><foo><bar>text node</bar></foo></root>");
transformer.setCargo(new CounterHolder());
transformer.registerBeforeAttributesHandler("*", (context, cargo, writer) -> {
cargo.counter++;
writer.writeAttribute("id", cargo.counter.toString());
return null;
});
String outputXml = transformer.transform();
Although the above code might be a little dangerous - if any element already has an @id
attribute. So we should suppress the existing @id
attributes...
class CounterHolder {
Long counter = 0L;
}
Optimus<CounterHolder> transformer = new Optimus<CounterHolder>("<root><foo><bar>text node</bar></foo></root>");
transformer.setCargo(new CounterHolder());
transformer.registerBeforeAttributesHandler("*", (context, cargo, writer) -> {
cargo.counter++;
writer.writeAttribute("id", cargo.counter.toString());
return null;
});
transformer.registerAttributeHandler("@id", (context, cargo, writer) -> {
// suppress any existing @id attrtibutes...
return ContinueState.SKIP_THIS;
});
String outputXml = transformer.transform();
Optimus can also be used to collect data from an input XML, in which case the output XML is of no interest - so a null transform is provided. For example, collecting all of the text from within <foo>
elements...
String inputXml = "<root><foo>some text</foo><foo>some more text</foo><foo>yet more text</foo><bar>not this though</bar></root>";
StringBuilder textCollector = new StringBuilder();
Optimus<StringBuilder> transformer = new Optimus<StringBuilder>(inputXml);
transformer.setCargo(textCollector);
transformer.registerCharactersHandler("foo/#text()", (context, cargo, writer) -> {
cargo.append(context.getText()).append("\n");
return null;
});
transformer.nullTransform();
assertEquals("some text\nsome more text\nyet more text\n", textCollector.toString());
see Optimus javadocs