简述
: JDK早期版本就自带JAXB转换功能,一直以来都是JAVA集大成之作,可所谓是JAVA之重器,但是很多时候我们使用过程中并不需要太过复杂可能只是简单的XML序列化和反序列化,今天我就解析XML和生成XML过程中遇到的问题再次记录,希望更多的小伙伴能够找到问题的解决办法.
首先是序列化XML,JAVA提供了以下注解用于描述一个实体对象转化到XML的过程
基本用法
@XmlRootElement
该注解用于类级别,对应xml的根元素,常与 @XmlType 和 @XmlAccessorType一起使用,主要的作用是映射xml的根节点,name属性标注根节点的名称.
@XmlElement
将Java对象的属性映射为xml的节点,在使用@XmlElement时,可通过name属性改变java对象属性在xml中显示的名称。@XmlElement(name="class") private String clz;
@XmlElements
当节点下面包含多个相同的子节点时,该注解可以映射成一个JavaBean的集合属性,其中注解中value的属性对应的是一个@XmlElement的集合
@XmlElements(value = {@XmlElement(name = "select", type = Select.class)}) private List<Select> selects;
@XmlAccessorType
用于指定由java对象生成xml文件时对java对象属性的访问方式。常与@XmlRootElement、@XmlType一起使用。它的属性值是XmlAccessType的4个枚举值,分别为:- XmlAccessType.FIELD:java对象中的所有成员变量
- XmlAccessType.PROPERTY:java对象中所有通过getter/setter方式访问的成员变量
- XmlAccessType.PUBLIC_MEMBER:java对象中所有的public访问权限的成员变量和通过getter/setter方式访问的成员变量
- XmlAccessType.NONE:java对象的所有属性都不映射为xml的元素
注意:@XmlAccessorType的默认访问级别是XmlAccessType.PUBLIC_MEMBER,因此,如果java对象中的private成员变量设置了public权限的getter/setter方法,就不要在private变量上使用@XmlElement和@XmlAttribute注解,否则在由java对象生成xml时会报同一个属性在java类里存在两次的错误。同理,如果@XmlAccessorType的访问权限为XmlAccessType.NONE,如果在java的成员变量上使用了@XmlElement或@XmlAttribute注解,这些成员变量依然可以映射到xml文件。
注意:虽然@XmlAccessorType为XmlAccessType.NONE,但是在java类的私有属性上加了@XmlAttribute和@XmlElement注解后,这些私有成员会映射生成xml的元素
@XmlAccessorOrder
用于对java对象生成的xml元素进行排序。它有两个属性值:
AccessorOrder.ALPHABETICAL:对生成的xml元素按字母书序排序
XmlAccessOrder.UNDEFINED:不排序@XmlTransient
用于标示在由java对象映射xml时,忽略此属性。即,在生成的xml文件中不出现此元素。注意XmlAccessType.Filed才会忽略属性,如果是property需要作用于getter方法@XmlJavaTypeAdapter
常用在转换比较复杂的对象时,如map类型或者格式化日期等。使用此注解时,需要自己写一个adapter类继承XmlAdapter抽象类,并实现里面的方法。
注意避坑
避坑1:XML转换到Java对象根元素带有命名空间比如
类似这种申明直接反序列化会报错:
javax.xml.bind.UnmarshalException: unexpected element (uri:””, local:”messages”). Expected elements are <{http://www.neusoft.com/hit/rhin}messages>
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(UnmarshallingContext.java:712)这里我需要重点介绍
工具类
NamespaceFilter的用法,很多情况下我们在解析XML到Java对象的时候NamespaceFilter提供了自定义SAXReader解析器的方法跳过命名空间可以正常解析XML
避坑2:
如果你希望转换到XML输出空节点,那么对象的property一定要赋初始值,比如String对象赋””
工具类以及示例代码
以下工具类重点关注:
- MarshallerListener空值情况下输出空节点信息
- marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);不处理header信息
- sax.setNamespaceAware(false);不处理命名空间
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLFilterImpl;
import org.xml.sax.helpers.XMLReaderFactory;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.Source;
import javax.xml.transform.sax.SAXSource;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.lang.reflect.Field;
/**
* Jaxb2工具类
*
* @author zhuc
* @create 2013-3-29 下午2:40:14
*/
public class JaxbUtil {
public static String convertToXml(Object obj) {
// 创建输出流
StringWriter sw = new StringWriter();
try {
// 利用jdk中自带的转换类实现
JAXBContext context = JAXBContext.newInstance(obj.getClass());
Marshaller marshaller = context.createMarshaller();
// 格式化xml输出的格式
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.FALSE);
// 是否省略xml头信息 默认否
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
JaxbUtil.MarshallerListener marlistener=new JaxbUtil().new MarshallerListener();
marshaller.setListener(marlistener);
// 将对象转换成输出流形式的xml
marshaller.marshal(obj, sw);
} catch (JAXBException e) {
e.printStackTrace();
}
return sw.toString();
}
/**
* xml文件配置转换为对象
* @param xmlStr xml
* @param load java对象.Class
* @return java对象
* @throws JAXBException
* @throws IOException
*/
public static <T> T converyToJavaBean(String xmlStr,Class<T> load) {
try {
JAXBContext context = JAXBContext.newInstance(load);
Unmarshaller unmarshaller = context.createUnmarshaller();
StringReader sr = new StringReader(xmlStr);
SAXParserFactory sax = SAXParserFactory.newInstance();
sax.setNamespaceAware(false);
XMLReader xmlReader = sax.newSAXParser().getXMLReader();
Source source = new SAXSource(xmlReader, new InputSource(sr));
T object = (T) unmarshaller.unmarshal(source);
return object;
} catch (Exception exception) {
exception.printStackTrace();
}
return null;
}
class MarshallerListener extends Marshaller.Listener{
public static final String BLANK_CHAR = "";
@Override
public void beforeMarshal(Object source) {
super.beforeMarshal(source);
Field[] fields = source.getClass().getDeclaredFields();
for (Field f : fields) {
f.setAccessible(true);
try {
if (f.getType() == String.class && f.get(source) == null) {
f.set(source, BLANK_CHAR);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
运行示例代码
import lombok.Data;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "messages" )
@XmlAccessorType(XmlAccessType.PROPERTY)
@Data
public class ResponseMessage {
private Switchset switchset = new Switchset();
private Business business = new Business();
private Extendset extendset = new Extendset();
@Data
public static class Extendset {
private String token = "";
}
@Data
public static class Business {
private String standardcode = "";
private Returnmessage returnmessage = new Returnmessage();
private Returnset returnset = new Returnset();
private String datacompress = "";
private Businessdata businessdata = new Businessdata();
}
@Data
public static class Businessdata {
private String taskid = "";
}
@Data
public static class Returnset {
private String rettotal = "";
private String retpaging = "";
private String retpageindex = "";
private String retpageset = "";
}
@Data
public static class Returnmessage {
private String retcode = "";
private String rettext = "";
}
@Data
public static class Switchset {
private Visitor visitor = new Visitor();
private Serviceinf serviceinf = new Serviceinf();
private Provider provider = new Provider();
private String route = "";
private String process = "";
private Switchmessage switchmessage = new Switchmessage();
}
@Data
public static class Switchmessage {
private String messagecode = "";
private String messagetext = "";
}
@Data
public static class Provider {
private String targetorgan = "";
private String targetdomain = "";
}
@Data
public static class Serviceinf {
private String servicecode = "";
}
@Data
public static class Visitor {
private String sourceorgan = "";
private String sourcedomain = "";
}
}
生成的XML格式如下:
调用工具类执行序列化和反序列化
public static void main(String[] args) throws Exception {
String xmlStr = JaxbUtil.convertToXml(new ResponseMessage()) ;
System.out.println(xmlStr);
System.out.println(JaxbUtil.convertToBean(xmlStr);
}
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<messages>
<business>
<businessdata>
<taskid></taskid>
</businessdata>
<datacompress></datacompress>
<returnmessage>
<retcode></retcode>
<rettext></rettext>
</returnmessage>
<returnset>
<retpageindex></retpageindex>
<retpageset></retpageset>
<retpaging></retpaging>
<rettotal></rettotal>
</returnset>
<standardcode></standardcode>
</business>
<extendset>
<token></token>
</extendset>
<switchset>
<process></process>
<provider>
<targetdomain></targetdomain>
<targetorgan></targetorgan>
</provider>
<route></route>
<serviceinf>
<servicecode></servicecode>
</serviceinf>
<switchmessage>
<messagecode></messagecode>
<messagetext></messagetext>
</switchmessage>
<visitor>
<sourcedomain></sourcedomain>
<sourceorgan></sourceorgan>
</visitor>
</switchset>
</messages>
希望这篇文章能够帮助到你,如果你有疑问可以随时联系我!