Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JAXB marshalling: Filter values of leaf elements

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?

like image 682
MRalwasser Avatar asked Feb 17 '17 15:02

MRalwasser


1 Answers

You could post-process the resulting XML to remove the first character of the text content of each leaf nodes using XSLT with the next stylesheet:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="*">
        <xsl:copy>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>
    <!-- For each text content of leaf nodes (nodes without child nodes) -->
    <xsl:template match="*[not(*)]/text()">
        <!-- Skip the first character -->
        <xsl:value-of select="substring(., 2)"/>
    </xsl:template>
</xsl:stylesheet>

According to the size of your resulting XML you could either keep the result into memory before applying the stylesheet or store first the resulting XML into a temporary file.

Here is how your code could be assuming that the resulting XML can fit into memory:

// Create the marshaller for the class Book
JAXBContext jaxbContext = JAXBContext.newInstance(Book.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();

// Make the output being pretty printed
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

// Marshall the book instance and keep the result into a ByteArrayOutputStream
ByteArrayOutputStream out = new ByteArrayOutputStream();
jaxbMarshaller.marshal(book, out);

TransformerFactory factory = TransformerFactory.newInstance();
// Define the stylesheet to apply
Transformer transformer = factory.newTransformer(new StreamSource(xsltFile));
// Define the input XML content
Source text = new StreamSource(new ByteArrayInputStream(out.toByteArray()));
// Apply the stylesheet and store the content into outputFile
transformer.transform(text, new StreamResult(outputFile));

Output:

<?xml version="1.0" encoding="UTF-8"?>
<Book>
    <Title>ogasana Vijnana: the Science of Yoga</Title>
    <Author>hirendra Brahmachari</Author>
    <Date>966</Date>
</Book>
like image 81
Nicolas Filotto Avatar answered Nov 01 '22 06:11

Nicolas Filotto