JAXB将JAVA对象转换为XML时CDATA的问题
对Java对象转换为xml字符串时,如果有特殊字符如<>必须要是用<![CDATA[]]>来声明。我先尝试了使用XStream解决这个问题也有使用MOXy CDATA注解解决,最后发现,都存在一定的缺陷,甚至不能正常的实现该功能。
最终解决方案有两个,都可以解决这个问题,但是第一个方案由于引用了sun的专有API,在编译时会存在警告,使用了内部的API并不是值得推荐的。但是这里也贴出来,供大家参考。
sunAPI版:
首先继承XMLSerializer
- import com.sun.org.apache.xml.internal.serialize.OutputFormat;
- import com.sun.org.apache.xml.internal.serialize.XMLSerializer;
- import org.xml.sax.SAXException;
- import java.io.OutputStream;
- import java.util.regex.Pattern;
- public class CDataContentHandler extends XMLSerializer {
- private static final Pattern XML_CHARS = Pattern.compile("[<>&]");
- public CDataContentHandler( OutputStream output, OutputFormat format ) {
- super(output,format);
- }
- public void characters(char[] ch, int start, int length) throws SAXException {
- boolean useCData = XML_CHARS.matcher(new String(ch, start, length)).find();
- if (useCData) super.startCDATA();
- super.characters(ch, start, length);
- if (useCData) super.endCDATA();
- }
- }
实际的转换方法:
- public static String ojbectToXmlWithCDATA(Class clazz, Object obj) throws Exception {
- JAXBContext context = JAXBContext.newInstance(clazz);
- OutputFormat of = new OutputFormat();
- of.setOmitXMLDeclaration(true);
- of.setPreserveSpace(true);
- of.setIndenting(true);
- ByteArrayOutputStream op = new ByteArrayOutputStream();
- CDataContentHandler serializer = new CDataContentHandler(op, of);
- SAXResult result = new SAXResult(serializer.asContentHandler());
- Marshaller mar = context.createMarshaller();
- mar.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
- mar.marshal(obj, result);
- return op.toString("UTF-8");
- }
第二个方法是目前使用的方法,参考了一个国外同行的解决方法,这个解决方案将自定实现javax.xml.stream.xmlstreamwriter,没有第三方的库依赖:
执行类决定哪些地方需要添加CDATA:
- import javax.xml.stream.XMLStreamException;
- import javax.xml.stream.XMLStreamWriter;
- import java.util.regex.Pattern;
- /**
- * Implementation which is able to decide to use a CDATA section for a string.
- */
- public class CDataXMLStreamWriter extends DelegatingXMLStreamWriter
- {
- private static final Pattern XML_CHARS = Pattern.compile( "[&<>]" );
- public CDataXMLStreamWriter( XMLStreamWriter del )
- {
- super( del );
- }
- @Override
- public void writeCharacters( String text ) throws XMLStreamException
- {
- boolean useCData = XML_CHARS.matcher( text ).find();
- if( useCData )
- {
- super.writeCData( text );
- }
- else
- {
- super.writeCharacters( text );
- }
- }
- }
委托类:
- import javax.xml.namespace.NamespaceContext;
- import javax.xml.stream.XMLStreamException;
- import javax.xml.stream.XMLStreamWriter;
- /**
- * Delegating {@link XMLStreamWriter}.
- */
- abstract class DelegatingXMLStreamWriter implements XMLStreamWriter
- {
- private final XMLStreamWriter writer;
- public DelegatingXMLStreamWriter( XMLStreamWriter writer )
- {
- this.writer = writer;
- }
- public void writeStartElement( String localName ) throws XMLStreamException
- {
- writer.writeStartElement( localName );
- }
- public void writeStartElement( String namespaceURI, String localName ) throws XMLStreamException
- {
- writer.writeStartElement( namespaceURI, localName );
- }
- public void writeStartElement( String prefix, String localName, String namespaceURI ) throws XMLStreamException
- {
- writer.writeStartElement( prefix, localName, namespaceURI );
- }
- public void writeEmptyElement( String namespaceURI, String localName ) throws XMLStreamException
- {
- writer.writeEmptyElement( namespaceURI, localName );
- }
- public void writeEmptyElement( String prefix, String localName, String namespaceURI ) throws XMLStreamException
- {
- writer.writeEmptyElement( prefix, localName, namespaceURI );
- }
- public void writeEmptyElement( String localName ) throws XMLStreamException
- {
- writer.writeEmptyElement( localName );
- }
- public void writeEndElement() throws XMLStreamException
- {
- writer.writeEndElement();
- }
- public void writeEndDocument() throws XMLStreamException
- {
- writer.writeEndDocument();
- }
- public void close() throws XMLStreamException
- {
- writer.close();
- }
- public void flush() throws XMLStreamException
- {
- writer.flush();
- }
- public void writeAttribute( String localName, String value ) throws XMLStreamException
- {
- writer.writeAttribute( localName, value );
- }
- public void writeAttribute( String prefix, String namespaceURI, String localName, String value )
- throws XMLStreamException
- {
- writer.writeAttribute( prefix, namespaceURI, localName, value );
- }
- public void writeAttribute( String namespaceURI, String localName, String value ) throws XMLStreamException
- {
- writer.writeAttribute( namespaceURI, localName, value );
- }
- public void writeNamespace( String prefix, String namespaceURI ) throws XMLStreamException
- {
- writer.writeNamespace( prefix, namespaceURI );
- }
- public void writeDefaultNamespace( String namespaceURI ) throws XMLStreamException
- {
- writer.writeDefaultNamespace( namespaceURI );
- }
- public void writeComment( String data ) throws XMLStreamException
- {
- writer.writeComment( data );
- }
- public void writeProcessingInstruction( String target ) throws XMLStreamException
- {
- writer.writeProcessingInstruction( target );
- }
- public void writeProcessingInstruction( String target, String data ) throws XMLStreamException
- {
- writer.writeProcessingInstruction( target, data );
- }
- public void writeCData( String data ) throws XMLStreamException
- {
- writer.writeCData( data );
- }
- public void writeDTD( String dtd ) throws XMLStreamException
- {
- writer.writeDTD( dtd );
- }
- public void writeEntityRef( String name ) throws XMLStreamException
- {
- writer.writeEntityRef( name );
- }
- public void writeStartDocument() throws XMLStreamException
- {
- writer.writeStartDocument();
- }
- public void writeStartDocument( String version ) throws XMLStreamException
- {
- writer.writeStartDocument( version );
- }
- public void writeStartDocument( String encoding, String version ) throws XMLStreamException
- {
- writer.writeStartDocument( encoding, version );
- }
- public void writeCharacters( String text ) throws XMLStreamException
- {
- writer.writeCharacters( text );
- }
- public void writeCharacters( char[] text, int start, int len ) throws XMLStreamException
- {
- writer.writeCharacters( text, start, len );
- }
- public String getPrefix( String uri ) throws XMLStreamException
- {
- return writer.getPrefix( uri );
- }
- public void setPrefix( String prefix, String uri ) throws XMLStreamException
- {
- writer.setPrefix( prefix, uri );
- }
- public void setDefaultNamespace( String uri ) throws XMLStreamException
- {
- writer.setDefaultNamespace( uri );
- }
- public void setNamespaceContext( NamespaceContext context ) throws XMLStreamException
- {
- writer.setNamespaceContext( context );
- }
- public NamespaceContext getNamespaceContext()
- {
- return writer.getNamespaceContext();
- }
- public Object getProperty( String name ) throws IllegalArgumentException
- {
- return writer.getProperty( name );
- }
- }
示例方法:
- /**使用JAXB方式解决CDATA问题
- *
- * @throws Exception
- */
- public static String ojbectToXmlWithCDATA(Class clazz, Object obj) throws Exception {
- JAXBContext context = JAXBContext.newInstance(clazz);
- ByteArrayOutputStream op = new ByteArrayOutputStream();
- XMLOutputFactory xof = XMLOutputFactory.newInstance();
- XMLStreamWriter streamWriter = xof.createXMLStreamWriter(op);
- CDataXMLStreamWriter cdataStreamWriter = new CDataXMLStreamWriter(streamWriter);
- Marshaller mar = context.createMarshaller();
- mar.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
- mar.marshal(obj, cdataStreamWriter);
- cdataStreamWriter.flush();
- cdataStreamWriter.close();
- return op.toString("UTF-8");
- }
生成后的xml代码示例:
- <?xml version='1.0' encoding='utf-8'?>
- <businessMessages xmlns="http://aaa.xxx.com/schema/BusinessMessages">
- <businessMessage>
- <uuid>abcccc</uuid>
- <source>tms</source>
- <topic>type</topic>
- <bussinessNo>123</bussinessNo>
- <header></header>
- <body>
- <![CDATA[<pivotFlow xmlns="http://aaa.xxx.com/schema/PivotFlow"><orderId>112345</orderId><operPersonId>testOper</operPersonId><operTime>2016-02-16T16:13:40.364+08:00</operTime><status>testType</status><appendix>{"shipper":"testShip","carrierPhone":"1860132223","carrier":"testCarr"}</appendix></pivotFlow>]]>
- </body>
- </businessMessage>
- </businessMessages>
英文原文参考:http://blog.mi-ernst.de/2012/05/04/jaxb-and-cdata-sections/
博客来源:http://blog.csdn.net/wantken/article/details/50675549