JAXB编组:过滤叶元素的值

MRa*_*ser 8 java xml jaxb marshalling

我有一个相当复杂的JAXB树对象.对于每个叶节点,我需要过滤其实际值

例如

<Book>
    <Title>Yogasana Vijnana: the Science of Yoga</Title>
    <Author>Dhirendra Brahmachari</Author>
    <Date>1966</Date>
</Book>
Run Code Online (Sandbox Code Playgroud)

这里的叶节点将是Title,authorDate.
想象一下,我需要为这个JAXB模型编写一个编组文档,并为每个叶节点删除第一个字符:

<Book>
    <Title>ogasana Vijnana: the Science of Yoga</Title>
    <Author>hirendra Brahmachari</Author>
    <Date>966</Date>
</Book>
Run Code Online (Sandbox Code Playgroud)


什么是最好的方法?
我看到两个出发点,但是,我现在卡住了.

1.在JAXB模型中进行更改
是否存在一些遍历机制,我可以使用它来获取任何JAXB对象的叶元素(某种访问者模式或其他东西)?

2.进入编组
可能我们可以挂钩编组,例如使用XMLStreamWriter..

这种问题有优雅的解决方案吗?

Nic*_*tto 10

另一种基于类型装饰器的 方法XMLStreamWriter将简单地跳过文本内容的第一个字符,但是你不能将它限制为只有叶子节点,它将对所有文本内容应用相同的逻辑而不仅仅是叶子如果您的编组不会像您的示例中那样生成混合内容,那么这些节点就不会成为问题.实际上,如果您没有混合内容(文本内容和节点混合在一起),则只有叶节点可以包含文本内容.

你的装饰者可能是这样的:

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你的代码将是:

// 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));
Run Code Online (Sandbox Code Playgroud)


Nic*_*tto 6

您可以使用下一个样式表对结果进行后处理XML以删除每个叶节点的文本内容的第一个字符XSLT:

<?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>
Run Code Online (Sandbox Code Playgroud)

根据结果​​的大小,您XML可以在应用样式表之前将结果保留在内存中,或者将结果存储XML到临时文件中.

以下是您的代码如何假设结果XML可以适合内存:

// 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));
Run Code Online (Sandbox Code Playgroud)

输出:

<?xml version="1.0" encoding="UTF-8"?>
<Book>
    <Title>ogasana Vijnana: the Science of Yoga</Title>
    <Author>hirendra Brahmachari</Author>
    <Date>966</Date>
</Book>
Run Code Online (Sandbox Code Playgroud)