上一篇:Spring WebFlux集成EclipseLink MOXy处理XML(一)基础I/O处理
类对象输出
示例代码:
@XmlAccessorType(XmlAccessType.FIELD)
public class BaseParam {
@XmlCDATA
private String sign = null;
...
}
@XmlAccessorType(XmlAccessType.FIELD)
public class BaseResponse {
@XmlElement(name = "return_msg")
private String returnMsg = null;
...
}
@XmlRootElement(name = "xml")
@XmlAccessorType(XmlAccessType.FIELD)
public class XmlResponse extends BaseResponse {
@XmlPath(".")
private BaseParam baseParam;
private String rate = null;
...
}
代码最终得到如下的XML输出:
<xml>
<return_msg>OK></return_msg>
<sign> FDD167FAA73459FD921B144BAF4F4CA2 </sign>
<rate>3.1415926</rate>
</xml>
代码解析:
- 如前文,对于XML根元素绑定的类,其父类输出路径相同,而类对象则会增加一层路径。解决方法是增加
@XmlPath(".")
,意为指向本身路径。
如果不加@XmlPath
则会输出:
<xml>
<return_msg>OK></return_msg>
<baseParam>
<sign> FDD167FAA73459FD921B144BAF4F4CA2 </sign>
</baseParam>
<rate>3.1415926</rate>
</xml>
- 如果对于列表元素想要封装一层路径,则可以使用
@XmlElementWrapper
对列表元素进行封装。此处不展开,可以参考官网示例。需要注意这种封装与@XmlPath
路径的区别。
CDATA转义输出
有的XML规范(如,微信支付API)要求对XML元素的属性进行CDATA转义,可以通过添加@XmlCDATA
进行处理
示例代码:
@XmlRootElement(name = "xml")
@XmlAccessorType(XmlAccessType.FIELD)
public class XmlResponse {
@XmlCDATA
@XmlJavaTypeAdapter(EnumCDataAdapter.class)
@XmlElement(name = "return_code")
private ResultStatusEnum returnCode = null;
@XmlCDATA
@XmlElement(name = "return_msg")
private String returnMsg = null;
...
}
代码最终得到如下的XML输出:
<xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
</xml>
代码解析:
- 通过添加
@XmlCDATA
,对元素值进行转义处理。 -
Moxy的坑:Enum类型直接加
@XmlCDATA
时并没有效果。解决方法是需要添加一个转换类来转换成String类型。示例代码:
public class EnumCDataAdapter extends XmlAdapter<String, Enum<?>> {
@Override
public String marshal(Enum<?> v) {
return v.toString();
}
...
}
动态元素输出
有时候我们希望动态输出元素名,特别是列表值,这时可以通过@XmlVariableNode
注解实现。这里举两个例子:
示例代码:
@XmlRootElement(name = "xml")
@XmlAccessorType(XmlAccessType.FIELD)
public class XmlResponse {
@XmlVariableNode("name")
public List<XmlElement> elements;
...
}
public static class XmlElement {
@XmlTransient
public String name;
@XmlCDATA
@XmlValue
public String value;
...
}
代码最终得到如下的XML输出:
<xml>
<0>valueA</0>
<1>valueB</1>
</xml>
代码解析:
- 通过添加
@XmlVariableNode
,匹配到XmlElement
类中的name
属性作为元素名输出。 -
@XmlTransient
通常用于显式要求结果输出中排除指定元素,这里用作配合@XmlVariableNode
生成动态元素名。 -
@XmlValue
用于将变量匹配于元素属性而非单独元素,这里绑定value
的值作为元素的属性。 - 可以通过方法修改
name
值改变元素名,比如增加前后缀等。需要将注解@XmlVariableNode
加到Getter
方法上即可。
Map输出
Moxy的坑:同一个类里面只支持一个@XmlVariableNode
元素。如果已经存在,其他的@XmlVariableNode
或者对象匹配注解如@XmlAnyElements
都无法同时使用。
解决方法是使用Map输出,Moxy对于Map没有原生支持,推荐做法是自己定义Map类型匹配。
示例代码:
public class MapAdapter extends XmlAdapter<AdaptedMap, Map<String, Integer>> {
/**
* We use only marshal function since multiple @XmlVariableNode not working in the same class
*
* @return Map
*/
@Override
public Map<String, Integer> unmarshal(AdaptedMap m) {
Map<String, Integer> map = new HashMap<>();
m.elements.forEach(i -> map.put(i.name, i.value));
return map;
}
@Override
public AdaptedMap marshal(Map<String, Integer> m) {
AdaptedMap map = new AdaptedMap();
map.elements = new ArrayList<>();
m.entrySet().forEach(e -> map.elements.add(new MapElement().build(e.getKey(), e.getValue())));
return map;
}
public static class AdaptedMap {
@XmlVariableNode("name")
List<MapElement> elements;
}
public static class MapElement {
@XmlTransient
public String name;
@XmlValue
public Integer value;
public MapElement build(String name, Integer value) {
this.name = name;
this.value = value;
return this;
}
}
}
@XmlRootElement(name = "xml")
@XmlAccessorType(XmlAccessType.FIELD)
public class XmlResponse {
@XmlPath(".")
@XmlJavaTypeAdapter(MapAdapter.class)
public Map<String, Integer> elements;
...
}
代码解析:
- 通过添加
@XmlJavaTypeAdapter
,匹配到MapAdapter
转换类。通过@XmlPath(".")
绑定当前类路径。 -
MapAdapter
的转换方式是将Map
转换成List
,从而使用@XmlVariableNode
方式进行输出。MapElement
类的实现方式同前,此处通过build
方法来为类对象赋值。