JAXB 编组:过滤叶元素的值
JAXB marshalling: Filter values of leaf elements
我有一个相当复杂的 JAXB 树对象。
对于每个叶节点,我需要过滤它的实际值
例如
<Book>
<Title>Yogasana Vijnana: the Science of Yoga</Title>
<Author>Dhirendra Brahmachari</Author>
<Date>1966</Date>
</Book>
此处的叶节点为 Title
、author
和 Date
。
想象一下,我需要为这个 JAXB 模型编写一个编组文档,每个叶节点的第一个字符都被删除:
<Book>
<Title>ogasana Vijnana: the Science of Yoga</Title>
<Author>hirendra Brahmachari</Author>
<Date>966</Date>
</Book>
最好的方法是什么?
我看到了两个起点,但是,我目前卡住了。
1.在 JAXB 模型中进行更改
是否有一些遍历机制可以用来获取任何 JAXB 对象(某种访问者模式或其他)的叶元素?
2。钩入编组
也许我们可以挂钩编组,例如使用 XMLStreamWriter
..
这种问题有没有优雅的解决方案?
您可以 post-process 结果 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>
根据结果 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));
输出:
<?xml version="1.0" encoding="UTF-8"?>
<Book>
<Title>ogasana Vijnana: the Science of Yoga</Title>
<Author>hirendra Brahmachari</Author>
<Date>966</Date>
</Book>
另一种方法基于 XMLStreamWriter
类型的 decorator,它会简单地跳过文本内容的第一个字符,但您无法对其进行限制仅对叶节点,它将对所有文本内容应用相同的逻辑,而不仅仅是叶节点,如果您的编组不像您的示例那样生成混合内容,这将不是问题。事实上,如果你没有混合内容(文本内容和节点混合在一起),只有叶节点可以有文本内容。
您的装饰器可能是这样的:
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);
}
}
}
那么您的代码将是:
// 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));
我有一个相当复杂的 JAXB 树对象。 对于每个叶节点,我需要过滤它的实际值
例如
<Book>
<Title>Yogasana Vijnana: the Science of Yoga</Title>
<Author>Dhirendra Brahmachari</Author>
<Date>1966</Date>
</Book>
此处的叶节点为 Title
、author
和 Date
。
想象一下,我需要为这个 JAXB 模型编写一个编组文档,每个叶节点的第一个字符都被删除:
<Book>
<Title>ogasana Vijnana: the Science of Yoga</Title>
<Author>hirendra Brahmachari</Author>
<Date>966</Date>
</Book>
最好的方法是什么?
我看到了两个起点,但是,我目前卡住了。
1.在 JAXB 模型中进行更改
是否有一些遍历机制可以用来获取任何 JAXB 对象(某种访问者模式或其他)的叶元素?
2。钩入编组
也许我们可以挂钩编组,例如使用 XMLStreamWriter
..
这种问题有没有优雅的解决方案?
您可以 post-process 结果 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>
根据结果 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));
输出:
<?xml version="1.0" encoding="UTF-8"?>
<Book>
<Title>ogasana Vijnana: the Science of Yoga</Title>
<Author>hirendra Brahmachari</Author>
<Date>966</Date>
</Book>
另一种方法基于 XMLStreamWriter
类型的 decorator,它会简单地跳过文本内容的第一个字符,但您无法对其进行限制仅对叶节点,它将对所有文本内容应用相同的逻辑,而不仅仅是叶节点,如果您的编组不像您的示例那样生成混合内容,这将不是问题。事实上,如果你没有混合内容(文本内容和节点混合在一起),只有叶节点可以有文本内容。
您的装饰器可能是这样的:
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);
}
}
}
那么您的代码将是:
// 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));