JAXB 使用记录
部分内容引自
https://blog.csdn.net/gengzhy/article/details/127564536
基础介绍
JAXBContext类:是应用的入口,用于管理XML/Java绑定信息
Marshaller接口:将Java对象序列化为XML数据
Unmarshaller接口:将XML数据反序列化为Java对象
@XmlRootElement:将Java类或枚举类型映射到XML元素,用在Java类上,用于标注该类是xml的一个根节点
@XmlElement:将Java类的一个属性映射到与属性同名的一个XML元素。通常与@XmlTransient搭配使用。
@XmlTransient:通常与 @XmlElement 须搭配使用的。@XmlElement用在属性上,用于指定生成xml的节点名,@XmlTransient用在对应的getter方法上,起到关联的作用
@XmlElementWrapper :对于数组或集合(即包含多个元素的成员变量),生成一个包装该数组或集合的XML元素(称为包装器)。通常配合XmlElement一起使用,XmlElementWrapper指定数组名,XmlElement指定生成xml的节点名
@XmlElementRef:用在类属性的getter方法上(即该属性是一个JavaBean),并且该属性是某些子类的父类,起到引用的作用。同时标注得有@XmlElementRef的类属性,其子类上需要使用@XmlRootElement标注,否则转换异常,提示找不到具体的引用实现。另外,转换时,需要将其子类的class一起传递到JAXBContext上下文中,否则也无法转换
@XmlAccessorOrder:控制JAXB 绑定类中属性和字段的排序
@XmlType:将Java类或枚举类型映射到XML模式类型
@XmlAccessorType(XmlAccessType.FIELD) :控制字段或属性的序列化。FIELD表示JAXB将自动绑定Java类中的每个非静态的(static)、非瞬态的(由@XmlTransient标 注)字段到XML。还有XmlAccessType.PROPERTY和XmlAccessType.NONE
@XmlJavaTypeAdapter:使用定制的适配器(即扩展抽象类XmlAdapter并覆盖marshal()和unmarshal()方法),以序列化Java类为XML
@XmlAttribute:将Java类的一个属性映射到与属性同名的一个XML属性
工具类
package xmlAndBean;
import lombok.extern.slf4j.Slf4j;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.Source;
import javax.xml.transform.sax.SAXSource;
import java.io.StringReader;
import java.io.StringWriter;
/**
* @version : 1.0
* @Description : xml工具类
* @Date: 2023/10/9 9:03
**/
public class XmlBeanUtils {
/**
* 构造方法私有
**/
private XmlBeanUtils() {
}
public static final String ENCODING_UTF = "UTF-8";
public static final String ENCODING_GB = "GB18030";
/**
* @return java.lang.String
* @Description : bean转xml不含报文头(GB18030编码格式)
* @Param [obj]
* @Param encoding 编码格式
**/
public static String beanToXml(Object obj,String encoding) {
String result = null;
try {
JAXBContext context = JAXBContext.newInstance(obj.getClass());
Marshaller marshaller = context.createMarshaller();
// Marshaller.JAXB_FORMATTED_OUTPUT 决定是否在转换成xml时同时进行格式化(即按标签自动换行,否则即是一行的xml)
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
// Marshaller.JAXB_ encoding xml的编码方式
marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding);
// 去掉生成xml的默认报文头
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
StringWriter writer = new StringWriter();
marshaller.marshal(obj, writer);
result = writer.toString();
} catch (Exception e) {
// log.error("bean转xml报文失败", e);
}
return result;
}
/**
* @return java.lang.String
* 报文格式:
* <?xml version="1.0" encoding="GB18030"?>
* <ROOT>
* <HEAD>
* …
* </HEAD>
* <DATA>
* <title1 >…</title1>
* <title2 >…</title2>
* …
* </DATA>
* </ROOT>
* @Description : 组装报文
* @Param [xmlHead, xmlData]
**/
public static String indentFormat(String xmlHead, String xmlData) {
try {
String xmlHeader = "<?xml version=\"1.0\" encoding=\"GB18030\"?>\n";
StringBuilder xml = new StringBuilder();
xml.append(xmlHeader).append("<ROOT>\n").append(xmlHead).append(xmlData).append("\n</ROOT>");
return xml.toString();
} catch (Exception e) {
// log.error("组装xml报文失败", e);
return null;
}
}
/**
* xml转对象
*
* @param xml
* @param msgVo
* @param <T>
* @return
*/
public static <T> T xmlToBean(String xml, Class<T> msgVo) {
if (msgVo == null) {
return null;
}
try {
JAXBContext context = JAXBContext.newInstance(msgVo);
Unmarshaller unmarshaller = context.createUnmarshaller();
Source source = trunSource(xml);
return (T) unmarshaller.unmarshal(source);
} catch (Exception e) {
// log.error("xml转对象异常:", e);
}
return null;
}
/**
* 忽略xml命名空间
*
* @param xmlStr
* @return
* @throws SAXException
* @throws ParserConfigurationException
*/
private static Source trunSource(String xmlStr) throws SAXException, ParserConfigurationException {
StringReader reader = new StringReader(xmlStr);
SAXParserFactory sax = SAXParserFactory.newInstance();
sax.setNamespaceAware(false);
XMLReader xmlReader = sax.newSAXParser().getXMLReader();
return new SAXSource(xmlReader, new InputSource(reader));
}
/**
* Java Bean 转 Xml
*
* @param bean - Java Bean
* @param inheritClazz - Java Bean中嵌套的类,且有继承关系的Java Class
* @return - xml
*/
public static String beanToXml(Object bean, String encoding,Class<?>... inheritClazz) {
try {
JAXBContext context = initContext(bean.getClass(), inheritClazz);
Marshaller marshaller = context.createMarshaller();
// 格式化xml
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
// Marshaller.JAXB_ encoding xml的编码方式
marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding);
// 去掉生成xml的默认报文头
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
StringWriter writer = new StringWriter();
marshaller.marshal(bean, writer);
return writer.toString();
} catch (JAXBException e) {
e.printStackTrace();
}
return null;
}
/**
* Xml 转 Java Bean
*
* @param xml - xml
* @param beanClazz - Java Bean Class
* @param inheritClazz - Java Bean中嵌套的类,且有继承关系的Java Class
* @return - bean
*/
public static Object xmlToBean(String xml, Class<?> beanClazz, Class<?>... inheritClazz) {
try {
JAXBContext context = initContext(beanClazz, inheritClazz);
Unmarshaller um = context.createUnmarshaller();
StringReader sr = new StringReader(xml);
return um.unmarshal(sr);
} catch (JAXBException e) {
e.printStackTrace();
}
return null;
}
/**
* 初始化JAXBContext
*
* @param mainClazz - 序列化或反序列化Class
* @param inheritClazz - Java Bean中嵌套的类,且有继承关系的Java Class
* @return - JAXBContext
*/
private static JAXBContext initContext(Class<?> mainClazz, Class<?>... inheritClazz) throws JAXBException {
JAXBContext context;
if (inheritClazz != null) {
Class<?>[] clazzArr = new Class[inheritClazz.length + 1];
clazzArr[0] = mainClazz;
System.arraycopy(inheritClazz, 0, clazzArr, 1, clazzArr.length - 1);
context = JAXBContext.newInstance(clazzArr);
} else {
context = JAXBContext.newInstance(mainClazz);
}
return context;
}
}
xml结构
结构如下:
<ROOT>
<HEAD>
…
</HEAD>
<DATA>
<title1 >…</title1>
<title2 >…</title2>
…
<RECORD>
<LIST1 p_type="G">
<title1 >…</title1>
</LIST1>
<LIST1 p_type="G">
<title1 >…</title1>
</LIST1>
</RECORD>
</DATA>
</ROOT>
①存在继承关系的bean转xml
基类:用于统一,方便向下转型
/**
* @version : 1.0
* @Description : 基类
* @Date: 2023/10/9 19:55
**/
public class BaseDTO {
}
DATA标签实体类同时继承基类
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import java.util.List;
/**
* @version : 1.0
* @Description :
* @Date: 2023/10/9 19:55
**/
@Setter
@Getter
@ToString
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "DATA")
public class BookDTO extends BaseDTO{
@XmlElement(name = "name")
@XmlJavaTypeAdapter(CdataXmlAdapter.class)
private String bookName;
@XmlElement(name = "id")
private String bookId;
//@XmlElementWrapper(name = "RECORD")
// @XmlElement(name = "LIST1")
// private List<DescDTO> descDTOList;
}
HEAD标签实体类
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
/**
* @version : 1.0
* @Description :
* @Date: 2023/10/9 19:58
**/
@Getter
@Setter
@ToString
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "HEAD")
public class HeadDTO {
@XmlElement(name = "SEND_NODE")
private String send;
@XmlElement(name = "RECV_NODE")
private String receive;
}
根标签实体类
其中针对DATA标签,每个接口的DATA标签可能都不相同,这里使用了@XmlElementRef标签结合基类(BaseDTO ),在使用中将接口的实体类继承BaseDTO基类,并标注@XmlRootElement(name = “DATA”),即可实现每个接口拥有独立的对象实现bean转xml
import lombok.*;
import javax.xml.bind.annotation.*;
/**
* @version : 1.0
* @Description :
* @Date: 2023/10/9 19:55
**/
@Setter
@Getter
@ToString
@NoArgsConstructor
@AllArgsConstructor
@XmlAccessorType(XmlAccessType.FIELD)
//@XmlType(propOrder = { "head", "data" }) 这句加不加无所谓
@XmlRootElement(name = "ROOT")
public class RootDTO {
@XmlElement(name = "HEAD")
private HeadDTO head;
//@XmlElement(name = "DATA")
//@XmlElementRef 注解要加载对象上不能加在get方法上,不然报错如下
//com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 5 counts of IllegalAnnotationExceptions类的两个属性具有相同名称 "head"
@XmlElementRef(name = "DATA")
private BaseDTO data;
}
测试类
public class XmlTest {
public static void main(String[] args) {
//继承基类
BookDTO bookDTO = new BookDTO();
bookDTO.setBookName("name");
bookDTO.setBookId("id");
//HEAD标签实体类,一般内容固定
HeadDTO headDTO = new HeadDTO();
headDTO.setSend("send");
headDTO.setReceive("rece");
RootDTO rootDTO = new RootDTO();
rootDTO.setHead(headDTO);
//因为BookDTO 继承基类所以此处能设置成功
rootDTO.setData(bookDTO);
//bean转xml,需要将BookDTO.class(子类)传入以告知
System.out.println(XmlBeanUtils.beanToXml(rootDTO,XmlBeanUtils.ENCODING_GB,BookDTO.class));
//xml转bean
System.out.println(XmlBeanUtils.xmlToBean(xml, RootDTO.class,BookDTO.class));
}
}
运行结果:
②存在数组的bean转xml
DATA标签实体类同时继承基类
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import java.util.List;
/**
* @version : 1.0
* @Description :
* @Date: 2023/10/9 19:55
**/
@Setter
@Getter
@ToString
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "DATA")
public class BookDTO extends BaseDTO{
@XmlElement(name = "name")
@XmlJavaTypeAdapter(CdataXmlAdapter.class)
private String bookName;
@XmlElement(name = "id")
private String bookId;
//@XmlElementWrapper(name = "RECORD")
@XmlElement(name = "LIST1")
private List<DescDTO> descDTOList;
}
数组实体类对象
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import javax.xml.bind.annotation.*;
/**
* @version : 1.0
* @Description :
* @Date: 2023/10/10 10:25
**/
@Getter
@Setter
@ToString
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "LIST1")
public class DescDTO{
@XmlAttribute(name = "p_type")
private String pType;
@XmlElement(name = "description")
private String description;
}
假设在DATA标签实体类中存在数组属性,则使用@XmlElement(name = “LIST1”)进行标注,即可实现每个数组对象转换成xml时都被 包裹
测试类
public class XmlTest {
public static void main(String[] args) {
BookDTO bookDTO = new BookDTO();
bookDTO.setBookName("name");
bookDTO.setBookId("id");
HeadDTO headDTO = new HeadDTO();
headDTO.setSend("send");
headDTO.setReceive("rece");
RootDTO rootDTO = new RootDTO();
rootDTO.setHead(headDTO);
rootDTO.setData(bookDTO);
DescDTO descDTO = new DescDTO();
//descDTO.setPType("G");
descDTO.setDescription("desc11111");
DescDTO descDTO1 = new DescDTO();
//descDTO1.setPType("G");
descDTO1.setDescription("desc22222");
ArrayList<DescDTO> descDTOS = new ArrayList<>();
descDTOS.add(descDTO);
descDTOS.add(descDTO1);
bookDTO.setDescDTOList(descDTOS);
String xml = XmlBeanUtils.beanToXml(rootDTO, XmlBeanUtils.ENCODING_GB, BookDTO.class);
//bean转xml
System.out.println(XmlBeanUtils.beanToXml(rootDTO,XmlBeanUtils.ENCODING_GB,BookDTO.class));
//xml转bean
System.out.println(XmlBeanUtils.xmlToBean(xml, RootDTO.class,BookDTO.class));
}
}
运行结果
如果需要再在数组外面套一层标签则:
@XmlElementWrapper(name = "RECORD")
@XmlElement(name = "LIST1")
private List<DescDTO> descDTOList;
运行结果:
③存在数组且数组前后标签要求不一致的bean转xml
以标签开始,以标签结束
在数组对象中引入@XmlAttribute
@XmlAttribute(name = "p_type")
private String pType;
测试类:
//设置属性
descDTO.setPType("G");
public class XmlTest {
public static void main(String[] args) {
BookDTO bookDTO = new BookDTO();
bookDTO.setBookName("name");
bookDTO.setBookId("id");
HeadDTO headDTO = new HeadDTO();
headDTO.setSend("send");
headDTO.setReceive("rece");
RootDTO rootDTO = new RootDTO();
rootDTO.setHead(headDTO);
rootDTO.setData(bookDTO);
DescDTO descDTO = new DescDTO();
descDTO.setPType("G");
descDTO.setDescription("desc11111");
DescDTO descDTO1 = new DescDTO();
descDTO1.setPType("G");
descDTO1.setDescription("desc22222");
ArrayList<DescDTO> descDTOS = new ArrayList<>();
descDTOS.add(descDTO);
descDTOS.add(descDTO1);
bookDTO.setDescDTOList(descDTOS);
String xml = XmlBeanUtils.beanToXml(rootDTO, XmlBeanUtils.ENCODING_GB, BookDTO.class);
//bean转xml
System.out.println(XmlBeanUtils.beanToXml(rootDTO,XmlBeanUtils.ENCODING_GB,BookDTO.class));
//xml转bean
System.out.println(XmlBeanUtils.xmlToBean(xml, RootDTO.class,BookDTO.class));
}
}
运行结果: