标签:
一 前言
集成接口项目的开发,与第三方系统或中间平台等进行交互,支持服务端&客户端,支持 http/ https/ Webservice 等多种交互方式,数据传输主要采用XML(部分可能会在XML中以<![CDATA[......]]> 的形式包裹 JSON字符串),以后也可能会采用 JSON 格式。鉴于功能需要,必然要将XML传输的数据转换为JAVA对象来进行数据的处理,以及将JAVA对象转换为XML作为报文进行传输,因此,给出以下方案:利用JAXB技术来实现JAVA对象与XML的自由转换,以及对<![CDATA[......]]> 内容的处理。
根据业务与需求的复杂程度,本文中所列解决方案不能全部覆盖,因此会在今后进行补充。
二 JAXB概述
JAXB(Java Architecture for XML Binding) 是一个业界标准,是可根据XML Schema产生Java类的技术。同时,JAXB不仅提供了将XML反向生成Java对象树的方法,而且能将Java对象树重新写到
XML文档,实现Java对象与XML的互相转换。
Jaxb 2.0是JDK 1.6的组成部分,无需引入第三方jar包。Jaxb2.0使用了JDK的新特性,如:Annotation、泛型(GenericType)等
重要概念:
JAXBContext类,是应用的入口,用于管理XML/Java绑定信息。
Marshaller接口,将Java对象序列化为XML数据。
Unmarshaller接口,将XML数据反序列化为Java对象。
常用注解(annotation):
@XmlType,将Java类或枚举类型映射到XML Schema Type
@XmlAccessorType(XmlAccessType.FIELD) ,控制字段或属性的序列化。FIELD表示JAXB将自动绑定Java类中的每个非静态的(non-static)、非瞬态的(由@XmlTransient标注)字段到XML。其他值还有XmlAccessType.PROPERTY和XmlAccessType.NONE 等。
@XmlAccessorOrder,控制JAXB 绑定类中属性和字段的排序。
@XmlJavaTypeAdapter,使用定制的适配器(即扩展抽象类XmlAdapter并覆盖marshal()和unmarshal()方法),以序列化Java类为XML。
@XmlElementWrapper ,对于数组或集合(即包含多个元素的成员变量),生成一个包装该数组或集合的XML元素(称为包装器)。
@XmlRootElement,将Java类或枚举类型映射到XML元素。
@XmlElement,将Java类的一个属性映射到与属性同名的一个XML元素。
@XmlAttribute,将Java类的一个属性映射到与属性同名的一个XML属性。
@XmlTransient
1.@XmlType
@XmlType用在class类的注解,常与@XmlRootElement,@XmlAccessorType一起使用;
它有三个属性:name、propOrder、namespace,经常使用的只有前两个属性。如:
@XmlType(name = "basicStruct", propOrder = {"intValue","stringArray","stringValue")
在使用@XmlType的propOrder 属性时,必须列出JavaBean对象中的所有属性,否则会报错。
若同时使用了@XmlType(propOrder={})和 @XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL)的时候,生成的XML只会按照propOrder定义的顺序生成元素
2.@XmlRootElement
@XmlRootElement用于类级别的注解,对应XML的根元素,常与 @XmlType 和 @XmlAccessorType一起使用;
若制定属性name,则会指定其在XML中的显示的名称,如:
@XmlType
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name="ADDRESS_INFO")
public class Address {......}
3.@XmlElement
@XmlElement将java对象的属性映射为XML的节点,在使用@XmlElement时,可通过name属性改变java对象属性在XML中显示的名称。如:
@XmlElement(name="ADDRESS")
private String yourAddress;
4.@XmlAttribute
@XmlAttribute用于把java对象的属性映射为XML的属性,并可通过name属性为生成的xml属性指定别名。如:
@XmlAttribute(name="state_name")
private String state;
5.@XmlAccessorType
@XmlAccessorType用于指定由java对象生成xml文件时对java对象属性的访问方式。常与@XmlRootElement、@XmlType一起使用。它的属性值是XmlAccessType的4个枚举值,分别为:
注意:@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的元素
6.@XmlAccessorOrder
@XmlAccessorOrder用于对java对象生成的xml元素进行排序。它有两个属性值:
AccessorOrder.ALPHABETICAL:对生成的xml元素按字母书序排序
XmlAccessOrder.UNDEFINED:不排序
7.@XmlTransient
@XmlTransient用于标识在由java对象转换为XML时,忽略此属性,即,在生成的XML文件中不会出现该节点。
8.@XmlJavaTypeAdapter
@XmlJavaTypeAdapter常用在转换比较复杂的对象时,如map类型或者格式化日期等。使用此注解时,需要自己写一个adapter类继承XmlAdapter抽象类,并实现里面的方法。
@XmlJavaTypeAdapter(DateXmlAdapter.class)
DateXmlAdapter为自己定义的adapter类,来处理日期格式化问题
@XmlJavaTypeAdapter(DateXmlAdapter.class)
private Date createDate;
9.@XmlElementWrapper
注解表示生成一个包装 XML 表示形式的包装器元素。 此元素主要用于生成一个包装集合的包装器 XML 元素。
注:@XmlElementWrapper仅允许出现在集合属性上
三 实例
针对前言中所讲,下面我们将展开一个实例,来处理和XML相关的转换.
根据上面Jaxb概述,我们在java对象上均使用annotation来注解,为了能够更加清晰直观的展示javabean中的显示,针对对象属性的访问方式我们统一为
<span style="white-space:pre"> </span>@XmlAccessorType(XmlAccessType.FIELD)
首先,我们先准备好我们的javabean:
Employee Address Description
Employee.java
package com.moonreal.java.jaxb.bean;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import com.moonreal.java.jaxb.xmladapter.CDATAAdapter;
import com.moonreal.java.jaxb.xmladapter.DateXmlAdapter;
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name="EMPLOYEE_AS_ROOT")
//@XmlType(name = "employee", propOrder = { })
public class Employee implements Serializable{
/**
*
*/
private static final long serialVersionUID = -2203839474229426435L;
@XmlAttribute(name="emp_id", required = true)
// @XmlElement
private Integer id;
@XmlElement(name = "Employee_name", required = true)
private String name;
@XmlElement(required = true)
private String Gender="";
@XmlJavaTypeAdapter(DateXmlAdapter.class)
private Date hiringDate;
@XmlElementWrapper(name="ADDRESSES")
@XmlElement(name="address")
private List<Address> address = new ArrayList<Address>();
@XmlJavaTypeAdapter(CDATADescriptionAdapter.class)
private Description desc;
@XmlJavaTypeAdapter(CDATAStringAdapter.class)
private String jsonBlock;
// @XmlJavaTypeAdapter(MapXmlAdapter.class)
// @XmlElement(name="addresses")
// private Map<String,Address> addressMap;
//getter/setter methods and overrided toString() method
}
Address.java
package com.moonreal.java.jaxb.bean;
import java.io.Serializable;
import java.util.Date;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import com.moonreal.java.jaxb.xmladapter.DateXmlAdapter;
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement
public class Address implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1270713109637521319L;
private String type;
private String address;
@XmlJavaTypeAdapter(DateXmlAdapter.class)
private Date period;
<pre code_snippet_id="1682303" snippet_file_name="blog_20160513_5_4039453" name="code" class="java">//getter/setter methods and overrided toString() method}
Description.java
package com.moonreal.java.jaxb.bean;
import java.io.Serializable;
import java.util.Date;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.moonreal.java.jaxb.jsonSerializer.DateJsonSerializer;
import com.moonreal.java.jaxb.xmladapter.DateXmlAdapter;
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name="description")
public class Description implements Serializable{
/**
*
*/
private static final long serialVersionUID = -1749942836341705166L;
private String descName;
private String descMsg;
@XmlJavaTypeAdapter(DateXmlAdapter.class)
@JsonSerialize(using = DateJsonSerializer.class)
private Date createDate;
//getter/setter methods and overrided toString() method}处理XML与Java对象之间的转换:
package com.moonreal.java.jaxb;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import com.moonreal.java.jaxb.constant.CommonConstant;
import com.sun.xml.internal.bind.marshaller.CharacterEscapeHandler;
/**
* @author xuan.fan
* @create May 11, 2016 3:04:32 PM
*/
public class XMLUtil {
/**
* Convert from Object to XML <br/>
* Default encoding UTF-8
*
* @param obj
* @return
*/
public static String convertToXml(Object obj) {
return convertToXml(obj, CommonConstant.ENCODING_UTF8);
}
/**
* Convert from Object to XML
*
* @param obj
* @param encoding
* @return
*/
public static String convertToXml(Object obj, String encoding) {
String result = null;
StringWriter writer = new StringWriter();
try {
JAXBContext context = JAXBContext.newInstance(obj.getClass());
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding);
// marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.FALSE);
marshaller.setProperty("com.sun.xml.internal.bind.marshaller.CharacterEscapeHandler",
new CharacterEscapeHandler() {
public void escape(char[] chars, int start,
int length, boolean isAttVal,
Writer writer) throws IOException {
writer.write(chars, start, length);
}
});
marshaller.marshal(obj, writer);
// result = writer.toString();
// remove the standalone="yes" in the xml header
result = writer.toString().replace("standalone=\"yes\"", "");
} catch (Exception e) {
throw new RuntimeException(e);
}finally{
if(writer!=null){
try {
writer.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
return result;
}
/**
* Convert from XML to Object
*
* @param xml
* @param clazz
* @return
*/
@SuppressWarnings("unchecked")
public static <T> T convertToObject(String xml, Class<T> clazz) {
T _clazz = null;
StringReader reader = null;
try {
JAXBContext context = JAXBContext.newInstance(clazz);
Unmarshaller unmarshaller = context.createUnmarshaller();
reader = new StringReader(xml);
_clazz = (T) unmarshaller.unmarshal(reader);
} catch (Exception e) {
throw new RuntimeException(e);
}finally{
if(reader!=null){
reader.close();
}
}
return _clazz;
}
}
处理JSON与Java对象之间的转换:
JsonUtils.java
package com.moonreal.java.jaxb;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JsonUtils {
private final static ObjectMapper mapper = new ObjectMapper();
public static <I extends Object> String toJsonWithPrettyPrinter(I input) {
String result = null;
if (input != null) {
try {
result = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(input);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
return result;
}
public static <I extends Object> String toJson(I input) {
String result = null;
if (input != null) {
try {
result = mapper.writeValueAsString(input);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
return result;
}
public static <I extends Object> I fromJson(String str, Class<I> clazz) {
I result = null;
if (str!=null && !str.isEmpty()) {
try {
result = mapper.readValue(str, clazz);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return result;
}
}
考虑到,在javabean转换为XML的时候,某些类型如MAP,Date等,生成的格式并非我们期望的,因此我们需要一些适配器(adapter)来进一步的处理,这里列出两种不同的adapter,非别针对Date类型和特殊的CDATA数据:
DateXmlAdapter.java
package com.moonreal.java.jaxb.adapter;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import com.moonreal.java.jaxb.constant.CommonConstant;
/**
* Adapter for converting from Date to XML using JAXB, vice versa.
*
* @author xuan.fan
* @create May 11, 2016 2:59:34 PM
*/
public class DateXmlAdapter extends XmlAdapter<String, Date> {
private SimpleDateFormat dateFormat = new SimpleDateFormat(CommonConstant.DATE_FORMAT_TO_SECOND);
@Override
public Date unmarshal(String date) throws Exception {
return dateFormat.parse(date);
}
@Override
public String marshal(Date date) throws Exception {
return dateFormat.format(date);
}
}
CDATAAdapter.java
package com.moonreal.java.jaxb.xmladapter;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import com.moonreal.java.jaxb.JsonUtils;
import com.moonreal.java.jaxb.bean.Description;
public class CDATADescriptionAdapter extends XmlAdapter<String, Description> {
@Override
public Description unmarshal(String v) throws Exception {
Description o = JsonUtils.fromJson(v, Description.class);
return o;
}
@Override
public String marshal(Description v) throws Exception {
String jsonStr = JsonUtils.toJson(v);
return "<![CDATA[" + jsonStr + "]]>";
}
}
针对包含在上面处理的CDATA部分的Json字符串进行转换时,涉及到Date类型的数据,对于日期格式的显示问题处理,同JAXB一样,jackjson也需要一个类似adapter的serializer来进行格式化,如下:
DateJsonSerializer.java
package com.moonreal.java.jaxb.jsonSerializer;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.moonreal.java.jaxb.constant.CommonConstant;
/**
* Format Date
*
* @author xuan.fan
* @create May 13, 2016 11:48:40 AM
*/
public class DateJsonSerializer extends JsonSerializer<Date> {
private SimpleDateFormat dateFormat = new SimpleDateFormat(CommonConstant.DATE_FORMAT_TO_SECOND);
@Override
public void serialize(Date date, JsonGenerator generator,
SerializerProvider provider) {
try {
String formattedDate = dateFormat.format(date);
generator.writeString(formattedDate);
} catch (JsonProcessingException jpex) {
throw new RuntimeException(jpex);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
根据前面的准备工作,现在我们做一个测试,看下我们的输入输出结果
package com.moonreal.java.jaxb;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import com.moonreal.java.jaxb.bean.Address;
import com.moonreal.java.jaxb.bean.AppendCaseHeader;
import com.moonreal.java.jaxb.bean.Body;
import com.moonreal.java.jaxb.bean.Description;
import com.moonreal.java.jaxb.bean.Employee;
import com.moonreal.java.jaxb.bean.Header;
import com.moonreal.java.jaxb.bean.Packet;
public class MainTest {
public static void main(String[] args) {
Employee emp = new Employee();
emp.setId(123);
emp.setHiringDate(new Date());
emp.setName("xuan.fan");
Address a = new Address();
a.setAddress("sfdsf");
a.setPeriod(new Date());
Address a1 = new Address();
a1.setAddress("ssssss");
a1.setPeriod(new Date());
List<Address> list = new ArrayList<Address>();
list.add(a1);
list.add(a);
emp.setAddress(list);
String xml = XMLUtil.convertToXml(emp);
System.out.println(xml);
System.out.println("\n=============================");
Employee e = XMLUtil.convertToObject(xml, Employee.class);
System.out.println(e);
System.out.println("\n=============================");
StringBuffer sb = new StringBuffer();
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\" ?> ");
sb.append("<EMPLOYEE_AS_ROOT emp_id=\"123\"> ");
sb.append(" <Employee_name>xuan.fan</Employee_name> ");
sb.append(" <Gender></Gender> ");
sb.append(" <hiringDate>2016-05-12 16:57:31</hiringDate> ");
sb.append(" <hobits/> ");
sb.append(" <desc><![CDATA[{\"descName\":\"Michal Jackson\",\"descMsg\":\"Cannot coming Wuxi\",\"createDate\":\"2016-05-13\"}]]></desc> ");
sb.append("</EMPLOYEE_AS_ROOT> ");
Employee em = (Employee) XMLUtil.convertToObject(sb.toString(), Employee.class);
Description desc = em.getDesc();
if(desc!=null)
System.out.println(desc.getDescMsg());
System.out.println(em);
System.out.println("\n=============================");
desc = new Description();
desc.setCreateDate(new Date());
em.setDesc(desc);
String xml2 = XMLUtil.convertToXml(em);
System.out.println(xml2);
}
}
执行程序,输出结果为:
<?xml version="1.0" encoding="UTF-8" ?>
<EMPLOYEE_AS_ROOT emp_id="123">
<Employee_name>xuan.fan</Employee_name>
<Gender></Gender>
<hiringDate>2016-05-13 15:43:24</hiringDate>
<ADDRESSES>
<address>
<address>ssssss</address>
<period>2016-05-13 15:43:24</period>
</address>
<address>
<address>sfdsf</address>
<period>2016-05-13 15:43:24</period>
</address>
</ADDRESSES>
</EMPLOYEE_AS_ROOT>
=============================
Employee [id=123, name=xuan.fan, gender=, hiringDate=Fri May 13 15:43:24 CST 2016, addresses=[Address [type=null, address=ssssss, period=Fri May 13 15:43:24 CST 2016], Address [type=null, address=sfdsf, period=Fri May 13 15:43:24 CST 2016]], desc=null]
=============================
Cannot coming Wuxi
Employee [id=123, name=xuan.fan, gender=, hiringDate=Thu May 12 16:57:31 CST 2016, addresses=[], desc=Description [descName=Michal Jackson, descMsg=Cannot coming Wuxi, createDate=Fri May 13 08:00:00 CST 2016]]
=============================
<?xml version="1.0" encoding="UTF-8" ?>
<EMPLOYEE_AS_ROOT emp_id="123">
<Employee_name>xuan.fan</Employee_name>
<Gender></Gender>
<hiringDate>2016-05-12 16:57:31</hiringDate>
<ADDRESSES/>
<desc><![CDATA[{"descName":"Michal Jackson","descMsg":"Cannot coming Wuxi","createDate":"2016-05-13 08:00:00"}]]></desc>
</EMPLOYEE_AS_ROOT>四 开发
上面的实例如何,就不详细讲解了,有兴趣可以了解下。
我们在开发中,针对不同的业务场景,报文数据是不一样的,因此我们需要根据不同的场景创建不同的javabean来与之对应,以便进行相互转换
下面讲一下我们在开发中创建javabean需要遵循的原则:
1. 根据报文XML的结构,定义与之相匹配的 javabean,从根节点开始,所有子节点对应的类均要创建;
2. 所有对应的javabean均要实现接口 Serializable,例如:
public class Employee implements Serializable{.......}
3. 所有对应的javabean均在类上添加如下注解:
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name="XXXXXXX")
public class Employee implements Serializable{4. 所有对应的javabean中若存在Date类型的成员变量,则在该成员变量上添加如下注解:
<span style="white-space:pre"> </span>@XmlJavaTypeAdapter(DateXmlAdapter.class) private Date hiringDate;5. 若报文xml中存在 <![CDATA[ ...... ]]> 块,那么它对应的javabean中对应的成员变量则根据类型,必须添加如下注解:
<span style="white-space:pre"> </span>@XmlJavaTypeAdapter(CDATADescriptionAdapter.class) private Description desc;b.若成员变量为实例中的 String类型,那么javabean中对应的jsonBlock上要添加如下注解:
<span style="white-space:pre"> </span>@XmlJavaTypeAdapter(CDATAStringAdapter.class) private String jsonBlock;c. ......
6. XML区分大小写,若成员变量与对应的xml节点大小写不一致,或名称不一致,则必须添加@XmlElement(name="xxxxxx")注解,来显式的指定在xml中显示的名称,例如:
<span style="white-space:pre"> </span>@XmlElement(name = "Employee_name") private String name;7. 所有的集合类型成员变量,均需要在变量添加@XmlElementWrapper(name="XXX")注解,来确定在生成xml的时候,被XXX节点包裹,例如:
<span style="white-space:pre"> </span>@XmlElementWrapper(name="ADDRESSES") @XmlElement(name="address") private List<Address> address = new ArrayList<Address>();
8. 若某个成员变量在转换时无需转换,则在变量上添加注解 @XmlTransient 来标识;
9. 若javabean在转xml时,某一个成员变量 value=null,则转换生成的xml中没有对应的节点,若需要显示,请给出默认值,例如:String str="";
10. 若对应的javabean中的某一个成员变量,在转换为xml后,并不是以“节点”的形式展示,而是该javabean对应的“节点”的属性,则必须添加如下注解:
<span style="white-space:pre"> </span>@XmlAttribute(name="emp_id") private Integer id;11. 若对应的javabean中存在Map类型的成员变量,则必须在对应的变量上添加如下adapter注解(以后根据需要增加对Map类型的adapter):
<span style="white-space:pre"> </span>@XmlJavaTypeAdapter(MapXmlAdapter.class) <span style="white-space:pre"> </span>@XmlElement(name="addresses") <span style="white-space:pre"> </span>private Map<String,Address> addressMap;
1. 在报文请求过来,我们拿到报文xml之后,根据配置我们找到对应的javabean,以Employee为例
<span style="white-space:pre"> </span>String xml = ...... Employee em = XMLUtil.<strong>convertToObject</strong>(xml, Employee.class);2. 在发送请求报文时,根据我们的javabean生成相应的xml报文,以Employee为例
<span style="white-space:pre"> </span>Employee emp = ......; String xml = XMLUtil.convertToXml(emp);
五 总结
以上内容主要是提供一个jaxb进行javabean与xml互相转换的实例,通过该实例,能够大概了解jaxb的工作原理,以便为之后的工作提供便利。在“四 开发”章节,主要是对实际开发中统一编程风格,减少不必要的问题进行的开发原则约束。
若已经对jaxb有所了解,可以直接跳过,直接阅读第四章节。
标签:
原文地址:http://blog.csdn.net/momoyiye/article/details/51388345