用来记录一些原创性的总结
使用JAXB处理对象与XML进行绑定时,可能我们对象中的某个属性的值可能包含标签。虽然字符串在转换为XML的文本节点时会自动的对其中的标签进行转义,但是我们也可以对它进行不转译处理。对于接收方来讲,如果节点包含的内容是需要当做一段纯文本处理的,那么就应该采用<![CDATA[...]]>
形式把它包起来,不然就会被误会成当一个子节点来处理。先来看一个示例。
假设我们有一段XML文本需要转换为一个Java对象,Java对象的定义如下:
@XmlRootElement
public static class Response {
private int code;
private String message;
private String returnValue;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getReturnValue() {
return returnValue;
}
public void setReturnValue(String returnValue) {
this.returnValue = returnValue;
}
@Override
public String toString() {
return "Response [code=" + code + ", message=" + message + ", returnValue=" + returnValue + "]";
}
}
其中的returnValue是一段特殊的XML格式的文本,现假设我们的returnValue的值对应的XML元素的文本节点值是一段XML,比如<a>1</a>
,那么我们期望它是可以被赋值给returnValue属性的。我们进行如下测试:
@Test
public void test() {
String xml = "<response><code>200</code><message>Success</message><returnValue><a>1</a></returnValue></response>";
Response response = JAXB.unmarshal(new StringReader(xml), Response.class);
System.out.println(response);
}
结果输出如下:
Response [code=200, message=Success, returnValue=]
很明显,returnValue的值是一个空字符串,而不是我们期望的<a>1</a>
。这是因为<a>1</a>
被当做了returnValue的一个子标签。这就相当于returnValue是一个复杂对象,但实际上它就是一个String类型,JAXB在转换的时候会先调用String的无参构造方法创建一个String对象,然后解析到其子标签<a>1</a>
时会把它当做String的一个属性,尝试获取其set方法,但是该方法不存在,所以最终创建的就是一个空字符串(String的无参构造方法创建的是一个空字符串)。那如果我们期望它可以正确的把<a>1</a>
赋值给returnValue应该怎么办呢?只需要把<a>1</a>
用<![CDATA]]>
包起来即可,这样JAXB在转换时就知道它对应的是一段纯文本,且会自动去掉<![CDATA]]>
。
@Test
public void test() {
String xml = "<response><code>200</code><message>Success</message><returnValue><![CDATA[<a>1</a>]]></returnValue></response>";
Response response = JAXB.unmarshal(new StringReader(xml), Response.class);
System.out.println(response);
}
把<a>1</a>
用<![CDATA[]]>
包起来后,转换的效果如下:
Response [code=200, message=Success, returnValue=<a>1</a>]
returnValue已经被成功赋值了。进行unmarshal时我们需要包含标签的纯文本能够用<![CDATA[]]>
包起来,那就需要生产XML的一方能够在进行marshal的时候添加。这种重复逻辑如果在每个地方都得手动的加一遍会比较麻烦。我们可以通过定义一个XmlJavaTypeAdapter来实现自动的添加。为此我们需要定义一个可以自动添加<![CDATA[]]>
的XmlAdapter实现类,在实现其marshal方法时需要考虑到传递进来的值为null的场景。
public static class CDATAAdapter extends XmlAdapter<String, String> {
@Override
public String unmarshal(String v) throws Exception {
return v;
}
@Override
public String marshal(String v) throws Exception {
if (v != null) {
return "<![CDATA[" + v + "]]>";
}
return null;
}
}
然后在我们的Response的returnValue属性的get方法上加上@XmlJavaTypeAdapter
注解,并指定需要使用的XmlAdapter为上面实现的CDATAAdapter。
@XmlRootElement
public static class Response {
private int code;
private String message;
private String returnValue;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@XmlJavaTypeAdapter(CDATAAdapter.class)
public String getReturnValue() {
return returnValue;
}
public void setReturnValue(String returnValue) {
this.returnValue = returnValue;
}
@Override
public String toString() {
return "Response [code=" + code + ", message=" + message + ", returnValue=" + returnValue + "]";
}
}
这样我们在对Response类型的对象进行marshal时就会自动为returnValue加上<![CDATA[]]>
了,测试用例如下:
@Test
public void testMarshal() {
Response response = new Response();
response.setCode(200);
response.setMessage("success");
response.setReturnValue("<a><b>123</b></a>");
JAXB.marshal(response, System.out);
}
测试的输出结果如下:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<response>
<code>200</code>
<message>success</message>
<returnValue><![CDATA[<a><b>123</b></a>]]></returnValue>
</response>
我们可以看到我们给returnValue赋值的<a><b>123</b></a>
被自动的加上了<![CDATA[]]>
,但是其中的标签都自动被转译了。这是JAXB的默认行为。这将在后文介绍如何让JAXB不自动对包含标签的XML文本节点进行自动转译。
(本文由Elim写于2017年7月29日)