您的位置:首页 > 编程语言 > Java开发

JAXB将JAVA对象转换为XML时CDATA的问题

2016-02-16 16:43 941 查看
对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/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: