了解CDATA
在XML元素中,”<”和”&”是非法的,如果使用这些特殊字符,那么解析器在解析文档时会产生错误。为了避免此类错误,需要把”<”这类特殊字符替换为实体引用,如
<user>age < 25</user> //<user>age < 25</user>
在 XML 中有 5 个预定义的实体引用:
实体引用 | 符号 | 说明 |
---|---|---|
< | < | 小于 |
> | > | 大于 |
& | & | 和号 |
' | ‘ | 省略号 |
" | “ | 引号 |
注释:严格地讲,在 XML 中仅有字符 “<”和”&” 是非法的。省略号、引号和大于号是合法的,但是把它们替换为实体引用是个好的习惯。
而 CDATA
指的是不应由 XML 解析器进行解析的文本数据(Unparsed Character Data),CDATA
部分中的所有内容都会被解析器忽略,CDATA
部分由 <![CDATA[
开始,由 ]]>
结束。所以,我们可以使用 CDATA
标签帮我们忽略某些特殊字符。
XStream
是一个Java对象与XML互相转换的工具类库。
首先,先说说实现原理吧,由于对象不是所有字段都增加 CDATA
标签,我们可以使用自定义注解方式,在需要添加 CDATA
标签的字段加上注解,然后改写XPPDriver
,通过反射查找添加注解的field
,在输出XML文本时加上<![CDATA[
和]]>
。废话不多说……
实现
一、创建注解
创建自定义注解@XStreamCDATA
:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface XStreamCDATA {
}
二、创建XStreamFactory,改写XPPDriver
public class XStreamFactory {
public static final String CDATA_PREFIX = "<![CDATA[";
public static final String CDATA_SUFFIX = "]]>";
public static XStream getXStream() {
final NameCoder nameCoder = new NoNameCoder();
XStream xStream = new XStream(new XppDomDriver(nameCoder) {
@Override
public HierarchicalStreamWriter createWriter(Writer out) {
return new PrettyPrintWriter(out, nameCoder) {
boolean cdataFlag = false;
Class<?> targetClass = null;
@Override
public void startNode(String name, Class clazz) {
super.startNode(name, clazz);
if (targetClass == null) {
targetClass = clazz;
}
cdataFlag = isCDATA(targetClass, name);
}
@Override
public void writeText(QuickWriter writer, String text) {
if (cdataFlag) {
writer.write(Constants.CDATA_PREFIX);
writer.write(text);
writer.write(Constants.CDATA_SUFFIX);
} else {
writer.write(text);
}
}
};
}
});
return xStream;
}
private static boolean isCDATA(Class<?> clazz, String fieldAlias) {
//检查类本身
boolean cdataFlag = isExistCDATA(clazz, fieldAlias);
if (cdataFlag) {
return cdataFlag;
}
//继续检查父类
Class<?> superClazz = clazz.getSuperclass();
while (!superClazz.equals(Object.class)) {
cdataFlag = isExistCDATA(superClazz, fieldAlias);
if (cdataFlag) {
return cdataFlag;
}
superClazz = superClazz.getClass().getSuperclass();
}
return false;
}
/**
* 检查是否有 @XStreamCDATA 注解
* @param clazz clazz
* @param fieldAlias fieldAlias
* @return
*/
private static boolean isExistCDATA(Class<?> clazz, String fieldAlias) {
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (field.getAnnotation(XStreamCDATA.class) != null) {
XStreamAlias xStreamAlias = field.getAnnotation(XStreamAlias.class);
if (xStreamAlias != null && fieldAlias.equals(xStreamAlias.value())) {
return true;
} else {
if (fieldAlias.equals(field.getName())) {
return true;
}
}
}
}
return false;
}
}
三、创建工具类
public class XmlUtils {
private static final XStream xStream = XStreamFactory.getXStream();
/**
* 对象转xml
* @param obj 对象
* @return
*/
public static String toXml(Object obj) {
xStream.processAnnotations(obj.getClass());
return xStream.toXML(obj);
}
}
四、创建对象类
由于最近在做微信公众号开发,直接就拿一个类来做演示。字段添加@XStreamCDATA
注解:
@XStreamAlias("xml")
public class WechatOutTextMsg implements Serializable {
private static final long serialVersionUID = -1883441132514476532L;
/**
* 接收方账号(收到的OpenID)
*/
@XStreamAlias("ToUserName")
@XStreamCDATA
private String toUserName;
/**
* 开发者微信号
*/
@XStreamAlias("FromUserName")
@XStreamCDATA
private String fromUserName;
/**
* 消息创建时间(整形)
*/
@XStreamAlias("CreateTime")
private Long createTime;
/**
* 消息类型
*/
@XStreamAlias("MsgType")
@XStreamCDATA
private String msgType;
/**
* 回复的消息内容
*/
@XStreamAlias("Content")
@XStreamCDATA
private String content;
//Getter and Setter...
}
五、测试
public class XmlUtilsTest {
@Before
public void before() throws Exception {
}
@After
public void after() throws Exception {
}
/**
*
* Method: toXml(Object obj)
*
*/
@Test
public void testToXml() throws Exception {
WechatOutTextMsg outTextMsg = new WechatOutTextMsg();
outTextMsg.setContent("你好");
outTextMsg.setCreateTime((new Date()).getTime());
outTextMsg.setFromUserName("fromUserName");
outTextMsg.setMsgType("text");
outTextMsg.setToUserName("toUserName");
//转xml
String str = XmlUtils.toXml(outTextMsg);
System.out.println(str);
}
}
结果输出
<xml>
<ToUserName><![CDATA[toUserName]]></ToUserName>
<FromUserName><![CDATA[fromUserName]]></FromUserName>
<CreateTime>1511358600000</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[你好]]></Content>
</xml>