开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第3天,点击查看活动详情
前言
平时咱们进行开发项目的时候,将对象和XML文件进行相互转换的场景应该不少吧,而如果我们手写的话还是比较麻烦的,属性对应上节点,还要注意XML的格式。
今天就来介绍一个JDK自带的工具包:javax.xml.bind。
定义对象
如果如图这样的xml我们如何转换成对象呢。
可以看到xml中有四个节点
- TerminologyRequest
- Terminology
- TerminologyItemList
- TerminologyItem
虽然是四个节点,但TerminologyItemList这个是TerminologyItem的集合,所以实际上只对应我们三个对象。
然后我们就直接根据节点的属性定义对应的对象就好了。
@Data
@XmlRootElement(name = "TerminologyRequest")
public class TerminologyRequest implements Serializable {
private String senderid;
private String msgid;
private Terminology terminology;
public String getSenderid() {
return senderid;
}
public void setSenderid(String senderid) {
this.senderid = senderid;
}
public String getMsgid() {
return msgid;
}
public void setMsgid(String msgid) {
this.msgid = msgid;
}
@XmlElement(name = "Terminology")
public Terminology getTerminology() {
return terminology;
}
public void setTerminology(Terminology terminology) {
this.terminology = terminology;
}
}
@Data
public class Terminology implements Serializable {
private String valuecode;
private String valuename;
private String statuscode;
private String versioncode;
private String versionname;
private String authorcode;
private String authorname;
private List<TerminologyItem> TerminologyItemList;
public String getValuecode() {
return valuecode;
}
public void setValuecode(String valuecode) {
this.valuecode = valuecode;
}
public String getValuename() {
return valuename;
}
public void setValuename(String valuename) {
this.valuename = valuename;
}
public String getStatuscode() {
return statuscode;
}
public void setStatuscode(String statuscode) {
this.statuscode = statuscode;
}
public String getVersioncode() {
return versioncode;
}
public void setVersioncode(String versioncode) {
this.versioncode = versioncode;
}
public String getVersionname() {
return versionname;
}
public void setVersionname(String versionname) {
this.versionname = versionname;
}
public String getAuthorcode() {
return authorcode;
}
public void setAuthorcode(String authorcode) {
this.authorcode = authorcode;
}
public String getAuthorname() {
return authorname;
}
public void setAuthorname(String authorname) {
this.authorname = authorname;
}
@XmlElementWrapper(name = "TerminologyItemList")
@XmlElement(name = "TerminologyItem")
public List<TerminologyItem> getTerminologyItemList() {
return TerminologyItemList;
}
public void setTerminologyItemList(List<TerminologyItem> terminologyItemList) {
TerminologyItemList = terminologyItemList;
}
}
@Data
public class TerminologyItem implements Serializable {
private String itemcode;
private String itemname;
private String itemstatuscode;
public String getItemcode() {
return itemcode;
}
public void setItemcode(String itemcode) {
this.itemcode = itemcode;
}
public String getItemname() {
return itemname;
}
public void setItemname(String itemname) {
this.itemname = itemname;
}
public String getItemstatuscode() {
return itemstatuscode;
}
public void setItemstatuscode(String itemstatuscode) {
this.itemstatuscode = itemstatuscode;
}
}
- 首先我们需要对根节点对应的对象类上加上
@XmlRootElement注解并注明对应的节点名,请参考TerminologyRequest对象 - 然后对于自己的子节点对应的对象可以不加
@XmlRootElement但是需要在get方法上加上@XmlElement注解并注明对应的子节点名,请参考TerminologyRequest对象的getTerminology()方法 - 如果有个节点对应的并不是一个对象,他的节点下一个节点才是对象,比如集合。这时候需要在get方法上加上两个注解:
@XmlElementWrapper表示集合对应的节点,@XmlElement表示集合内部的对象,请参考Terminology对象的getTerminologyItemList()方法
转换
接着我们使用JAXBContext就可以进行对象和XML的相互转换了。
public static String beanToXml(Object obj, Class c) {
if (obj == null){
return null;
}
String xml = null;
try {
JAXBContext context = JAXBContext.newInstance(c);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
Writer w = new StringWriter();
marshaller.marshal(obj, w);
xml = w.toString();
} catch (Exception e) {
System.out.println(e);
}
return xml;
}
public static <T> T xmlToBean(String xml, Class<T> c) {
if (StringUtils.isEmpty(xml)){
return null;
}
T t = null;
try {
JAXBContext context = JAXBContext.newInstance(c);
Unmarshaller unmarshaller = context.createUnmarshaller();
t = (T) unmarshaller.unmarshal(new StringReader(xml));
} catch (Exception e) {
System.out.println(e);
}
return t;
}
包含null属性的转换
实际上,上面的转换会丢失掉一些节点属性,当对象属性为空的时候,转换成XML并不会显示出为空的属性。
针对这种情况,我们怎么办呢?
这就需要介绍到Marshaller中的一个内部类Listener了。
他实际上就是一个回调机制,定义了对象转换对象之前和之后的回调。
所以我们在转换之前就可以提前将空值的属性赋一个默认值,这样就能实现XML所需的属性了。
- 首先定义一个类继承
Marshaller.Listener - 然后实现
beforeMarshal接口 - 利用反射将空属性赋值默认值
public class MarshallerListener extends Marshaller.Listener {
@Override
public void beforeMarshal(Object source) {
super.beforeMarshal(source);
Field[] fields = source.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
try {
if (field.getType().equals(String.class) && field.get(source) == null){
field.set(source,"");
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
然后在Marshaller转换xml时设置这个监听器就可以了
marshaller.setListener(new MarshallerListener());
到此就能达到输出空属性的XML了。
总结
利用好javax.xml.bind包,咱们就能很简单的实现对象和XML的相互转换了。
文中如有不足之处,欢迎指正!一起交流,一起学习,一起成长 ^v^