Двойное подчеркивание в XStream - обработка в контексте приложения Java Spring Batch
Я использую XStream для Oxm (Java объектов в XML). NameCoder использует символ подчеркивания в качестве escape-символа по умолчанию (подробнее см. Ниже), и поскольку у меня ext_data в качестве тега xml, он записывается как ext__data.
XStream сопоставляет имена классов Java и имена полей с тегами или атрибутами XML. К сожалению, это отображение не может быть 1:1, поскольку некоторые символы, используемые для идентификаторов в Java, недопустимы в именах XML. Поэтому XStream использует anXmlFriendlyNameCoder для замены этих символов заменой. По умолчанию этот NameCoder использует подчеркивание в качестве escape-символа и поэтому должен также экранировать само подчеркивание. Вы можете предоставить другой сконфигурированный экземпляр XmlFriendlyNameCoder или полностью другую реализацию, такую как NoNameCoder, чтобы вообще запретить кодирование имени. Однако вы несете ответственность за то, чтобы полученные имена были действительными для XML. ( http://x-stream.github.io/faq.html)
Я попытался предоставить другой сконфигурированный экземпляр XMLFriendlyNameCoder (используя различные реализации HierarchicalStreamDriver), а также использовать совершенно другую реализацию NoNameCoder. Хотя приложение работает успешно, результаты остаются без изменений. Я использую XStream 1.4.8.
Есть много примеров того, как использовать предложенный обходной путь (вот пара, http://forum.spring.io/forum/spring-projects/batch/74500-getting-double-underscores-in-xml-when-using-staxeventitemwriter, XStream и подчеркивание), поэтому я вполне уверен, что мои различные попытки были настроены правильно (но я мог что-то упустить). Вот моя последняя конфигурация:
<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>
Я не совсем уверен, но, похоже, проблема в том, что Spring почему-то не принимает эту конфигурацию. Копая org.springframework.oxm.xstream.XStreamMarshaller, похоже, что streamDriver используется только внутри marshalOutputStream (и marshalWriter, вызываемый внутри marshalOutputStream), так что, возможно, я настроил это так, что пользовательская реализация игнорируется и используется streamDriver по умолчанию...
@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);
}
}
}
Я понимаю, что этот вопрос кажется уникальным и узким по объему, но ответы могут пролить свет на более широкий вопрос о том, как правильно настроить streamDriver в контексте приложения (в приложении Spring Batch).
Примечание. Я использую Spring 4 и Java 8.
Спасибо
Изменить: я также попытался использовать следующую конфигурацию:
<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>
1 ответ
Видимо, я поступил об этом неправильно. Ниже, кажется, работает:
<beans:bean id="myMarshaller" class="org.springframework.oxm.xstream.XStreamMarshaller">
<beans:property name="nameCoder">
<beans:bean class="com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder">
<beans:constructor-arg index="0" value="_-"/>
<beans:constructor-arg index="1" value="_"/>
</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>