I've got a fairly complex JAXB tree object. For every leaf node, I need to filter its actual value
E.g.
<Book>
<Title>Yogasana Vijnana: the Science of Yoga</Title>
<Author>Dhirendra Brahmachari</Author>
<Date>1966</Date>
</Book>
The leaf nodes here would be Title
,author
and Date
.
Imagine that I need a marshalled document for this JAXB model with the first character removed for every leaf node:
<Book>
<Title>ogasana Vijnana: the Science of Yoga</Title>
<Author>hirendra Brahmachari</Author>
<Date>966</Date>
</Book>
What is the best approach?
I see two starting points, however, I'm currently stuck.
1. Do the change in the JAXB model
Is there some traversal mechnism which I can use to get the leaf elements of any JAXB object (some kind of Visitor pattern or something)?
2. Hook into the marshalling
Maybe we can hook into marshalling, e.g. using a XMLStreamWriter
..
Is there an elegant solution for this kind of problem?
Another approach based on a decorator of type XMLStreamWriter
that will simply skip the first character of a text content however you won't have the ability to limit it to leaf nodes only, it will apply the same logic to all the text contents not only of leaf nodes which won't be an issue if your marshalling don't generate mixed contents like in your example. Indeed if you don't have mixed content (text content and nodes mixed together), only leaf nodes can have text content.
Your decorator could be something like this:
public class RemoveFirstCharacter implements XMLStreamWriter {
private final XMLStreamWriter delegate;
public RemoveFirstCharacter(final XMLStreamWriter delegate) {
this.delegate = delegate;
}
@Override
public void writeStartElement(final String localName) throws XMLStreamException {
delegate.writeStartElement(localName);
}
@Override
public void writeStartElement(final String namespaceURI, final String localName)
throws XMLStreamException {
delegate.writeStartElement(namespaceURI, localName);
}
...
@Override
public void writeCharacters(final String text) throws XMLStreamException {
// Skip the first character
delegate.writeCharacters(text.substring(1));
}
@Override
public void writeCharacters(final char[] text, final int start, final int len)
throws XMLStreamException {
if (start == 0) {
// Skip the first character
delegate.writeCharacters(text, 1, len - 1);
} else {
delegate.writeCharacters(text, start, len);
}
}
}
Then your code would be:
// Create the marshaller for the class Book
JAXBContext jaxbContext = JAXBContext.newInstance(Book.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
// Create the main XMLStreamWriter
XMLOutputFactory output = XMLOutputFactory.newInstance();
XMLStreamWriter writer = output.createXMLStreamWriter(System.out);
// Apply the custom XMLStreamWriter that will remove the first character
// of each text content
jaxbMarshaller.marshal(book, new RemoveFirstCharacter(writer));