近日在对接接口是报文需要是xml格式,所以今天小小总结下这段时间的收获。
首先介绍下jaxb,是java自带的将java实体类和xml文件转换的工具类。
使用时需要用到注解大致如下:
@XmlRootElement:根节点注解也是最基本的注解 他有个name属性可以指定根节点的名字; 使用在类上
@XmlAccessorType:指明以什么标准生成xml,value的值有意向几种:
1.XmlAccessType.FIELD:java对象中的所有成员变量
2.XmlAccessType.PROPERTY:java对象中所有通过getter/setter方式访问的成员变量
3.XmlAccessType.PUBLIC_MEMBER:java对象中所有的public权限的成员变量和通过getter/setter方式访问的成员变量
4.XmlAccessType.NONE:java对象的所有属性都不映射为xml的元素
默认值为PUBLIC_MEMBER
@XmlElement:属性节点的注解 也有name属性可以指定属性节点的名字,在xml中的表现为@XmlElement为
@XMLRootElement的子节点;使用在成员字段或者方法上
注:在使用@XmlElement的时候需要注意下,当你想在成员变量上使用@XMLElement注解时需要再class
加上XmlAccessorType,并且声明为FIELD
@XmlType:当xml报文需要指定节点的循序时,可以通过这个注解他有个proorder属性可以自定义的顺序
(自定义时必须所有的字段都指定不然就会报错,而且指定顺序时不时用xml中的标签名而是字段名。)
例:
@XmlElement(name="na")
private String name;//在XmlType中proOrder指定的是name儿不时na
@XmlElementWrapper(name="XXX"):用在lis/map上 ,一般都会配合@XmlElement使用,效果就是在xml中有
一个大标签(XmlElementWrapper)里面好多小标签(XmlElement),加入集合或者Map有数据的话
例:
//该注解适合在list , set map 的上边使用便利集合生成xml
@XmlElementWrapper(name="npts:elements")
@XmlElement(name = "npts:element")//该注解指定属性在xml中的标签 , 可以配合上边的注解生成嵌套
public List getElements() {return elements;}
效果:
10
天王星
nv
10
金星
nv
10
木星
nv
10
泰坦星
nv
当然如果你的报文中只需要许多的element标签也可以实现,只需要把XmlElementWrapper注释掉
例:
//该注解适合在list , set map 的上边使用便利集合生成xml
//@XmlElementWrapper(name="npts:elements")
@XmlElement(name = "npts:element")//该注解指定属性在xml中的标签 , 可以配合上边的注解生成嵌套
public List getElements() {return elements;}
效果:
10
天王星
nv
10
天王星
nv
10
金星
nv
10
木星
nv
10
泰坦星
nv
由此之上的使用起来一般不会有什么问题:
下面是我使用的时候遇到的问题:
如果你把上边的注解玩了玩,会发现@XmlElement有一个属性为requare可以设置为true或false;
网友说这个作用是设置为true是强制生成标签,但实际发现并不行。比如:某个成员变量的类型为String且在转换为xml的时候该变量的值为null 就不会产生该标签 ,但有的报文是需要的怎么办?。
不过有一个折中的方法就是给变量赋初值:"";这样可以完成需求 ,但是不够优雅 。后来我留意到在使用Marshaller转换时可以设置属性:
Marshaller m = context.createMarshaller();
// 是否格式化生成的xml串
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
//设置字符集
m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
// 是否省略xm头声明信息
m.setProperty(Marshaller.JAXB_FRAGMENT, false);
上边这些都不是解决方法哦。嘿嘿不过看了也不亏哈
Marshaller 还有个属性setListener(Listener listener)
Listener是抽象类,他有两个方法
顾名思义:和两个一个是在转换之前要做什么事情,一个是在转换之后做什么事前。两个方法的参数都是要转换的对象。
但是这两个方法体都是空的。
所以这里是他的实现类,重写这两个方法来完成我们想要做什么所以我就定义了一个类继承Listener。我们需要把刚提到的给为null的字段赋初值的动作方到这不就可以“优雅起来了”哈哈啊哈~
下面是重写方法:
//反射获取所有字段
Field[] fieldArr = source.getClass().getDeclaredFields();
int len = fields.length;
//遍历为该对象成员字段属性为String的赋初值
for(int i = 0; i < len; ++i) {
Field f = fieldArr[i];
//设置可操控属性
f.setAccessible(true);
try {
if (f.getType() == String.class && f.get(source) == null) {
f.set(source, "");
}
} catch (IllegalAccessException var8) {
var8.printStackTrace();
}
}
super.beforeMarshal(source);
然后还没完需要别忘了,我们需要把它用起来。
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty("jaxb.encoding", charset);
marshaller.setProperty("jaxb.formatted.output", true);
marshaller.setProperty("jaxb.fragment", false);
//将自定义的Listener设置到Listener中
marshaller.setListener(new SkeletonMarshallerListener());
marshaller.marshal(object, nsfFilter);
效果:
前:
10
nv
后:
10
nv
ok;如果能帮到您最好不过,当然有什么不对的地方渴求大家指正。现行谢过