I am using XStream for oxm (Java Objects to xml). NameCoder uses underscore as the default escape character (more info below) and since I have ext_data as an xml tag, it is being written as ext__data.
XStream maps Java class names and field names to XML tags or attributes. Unfortunately this mapping cannot be 1:1, since some characters used for identifiers in Java are invalid in XML names. Therefore XStream uses anXmlFriendlyNameCoder to replace these characters with a replacement. By default this NameCoder uses an underscore as escape character and has therefore to escape the underscore itself also. You may provide a different configured instance of the XmlFriendlyNameCoder or a complete different implementation like theNoNameCoder to prevent name coding at all. However it is your responsibility then to ensure, that the resulting names are valid for XML. (http://x-stream.github.io/faq.html)
I have attempted providing a different configured instance of XMLFriendlyNameCoder (using various implementations of HierarchicalStreamDriver) as well as using a complete different implementation of NoNameCoder. Although the app runs successfully, the results remain unchanged. I am using XStream 1.4.8.
There are plenty of examples of how to use the suggested workaround (here are a couple, http://forum.spring.io/forum/spring-projects/batch/74500-getting-double-underscores-in-xml-when-using-staxeventitemwriter, XStream and underscores) so I am fairly confident my various attempts have been set up correctly (but I could be overlooking something). Here is my most recent configuration:
<beans:bean id="myMarshaller" class="org.springframework.oxm.xstream.XStreamMarshaller">
<beans:property name="streamDriver">
<beans:bean class="com.thoughtworks.xstream.io.xml.Xpp3Driver">
<beans:constructor-arg>
<beans:bean class="com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder">
<beans:constructor-arg index="0" value="DDD"/>
<beans:constructor-arg index="1" value="_"/>
</beans:bean>
</beans:constructor-arg>
</beans:bean>
</beans:property>
<beans:property name="fieldAliases">
<util:map id="aliases">
<beans:entry key="com.billup.beans.Data.extData" value="ext_data"/>
</util:map>
</beans:property>
</beans:bean>
I'm not really sure but the issue seems to be that Spring isn't picking up this configuration for some reason. Digging through org.springframework.oxm.xstream.XStreamMarshaller it looks like streamDriver is only used within marshalOutputStream (and marshalWriter, called within marshalOutputStream) so perhaps I have this configured in such a way that the custom implementation is ignored and the default streamDriver is used...
@Override
protected void marshalXmlEventWriter(Object graph, XMLEventWriter eventWriter) throws XmlMappingException {
ContentHandler contentHandler = StaxUtils.createContentHandler(eventWriter);
marshalSaxHandlers(graph, contentHandler, null);
}
@Override
protected void marshalXmlStreamWriter(Object graph, XMLStreamWriter streamWriter) throws XmlMappingException {
try {
marshal(graph, new StaxWriter(new QNameMap(), streamWriter));
}
catch (XMLStreamException ex) {
throw convertXStreamException(ex, true);
}
}
@Override
protected void marshalOutputStream(Object graph, OutputStream outputStream) throws XmlMappingException, IOException {
if (this.streamDriver != null) {
marshal(graph, this.streamDriver.createWriter(outputStream));
}
else {
marshalWriter(graph, new OutputStreamWriter(outputStream, this.encoding));
}
}
@Override
protected void marshalSaxHandlers(Object graph, ContentHandler contentHandler, LexicalHandler lexicalHandler)
throws XmlMappingException {
SaxWriter saxWriter = new SaxWriter();
saxWriter.setContentHandler(contentHandler);
marshal(graph, saxWriter);
}
@Override
protected void marshalWriter(Object graph, Writer writer) throws XmlMappingException, IOException {
if (this.streamDriver != null) {
marshal(graph, this.streamDriver.createWriter(writer));
}
else {
marshal(graph, new CompactWriter(writer));
}
}
/**
* Marshals the given graph to the given XStream HierarchicalStreamWriter.
* Converts exceptions using {@link #convertXStreamException}.
*/
private void marshal(Object graph, HierarchicalStreamWriter streamWriter) {
try {
getXStream().marshal(graph, streamWriter);
}
catch (Exception ex) {
throw convertXStreamException(ex, true);
}
finally {
try {
streamWriter.flush();
}
catch (Exception ex) {
logger.debug("Could not flush HierarchicalStreamWriter", ex);
}
}
}
I realize this question seems unique and narrow in scope but responses may shed light on the larger question of how to properly configuring streamDriver within the application context (in a Spring Batch app).
Note- I am using Spring 4 and Java 8.
Thanks
Edit: I've also tried using the following configuration:
<beans:bean id="myMarshaller" class="org.springframework.oxm.xstream.XStreamMarshaller">
<beans:property name="streamDriver">
<beans:bean class="com.thoughtworks.xstream.io.xml.DomDriver">
<beans:constructor-arg value="UTF-8"/>
<beans:constructor-arg>
<beans:bean class="com.thoughtworks.xstream.io.naming.NoNameCoder"/>
</beans:constructor-arg>
</beans:bean>
</beans:property>
<beans:property name="fieldAliases">
<util:map id="aliases">
<beans:entry key="com.billup.beans.Data.extData" value="ext_data"/>
</util:map>
</beans:property>
</beans:bean>