之前记录过一篇java利用dom4j读取xml
jdk里面自带了jaxb也可以用来处理xml,利用注解的形式自动实现实体类和xml的互转,对于静态xml来说,这样会比较方便,无需知道具体实现的过程,也不用自己去写代码遍历Element。
下面记录一个例子:
新建XmlTestEntity实体类:
package com.zhaohy.app.entity;
import java.time.LocalDateTime;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
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.XmlType;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import com.zhaohy.app.common.LocalDateTimeAdapter;
@XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
@XmlRootElement(name = "Test")
@XmlType(propOrder = {"name", "elements", "elementName", "list", "dateTime"})
public class XmlTestEntity {
private String name;
private String elementName;
private List elements;
private List list;
private LocalDateTime dateTime = LocalDateTime.now();
public String getName() {
return name;
}
@XmlAttribute(name = "Name")
public void setName(String name) {
this.name = name;
}
public List getElements() {
return elements;
}
@XmlElementWrapper(name = "Elements")
@XmlElement(name = "Element")
public void setElements(List elements) {
this.elements = elements;
}
public String getElementName() {
return elementName;
}
@XmlElement(name = "Ele")
public void setElementName(String elementName) {
this.elementName = elementName;
}
public List getList() {
return list;
}
@XmlElement(name = "List")
public void setList(List list) {
this.list = list;
}
public LocalDateTime getDateTime() {
return dateTime;
}
@XmlJavaTypeAdapter(LocalDateTimeAdapter.class)
public void setDateTime(LocalDateTime dateTime) {
this.dateTime = dateTime;
}
}
新建实体类中用到的引入类SubElement
package com.zhaohy.app.entity;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlTransient;
public class SubElement {
private String value;
@XmlAttribute(name = "Name")
private String name;
public String getValue() {
return value;
}
@XmlElement(nillable = true, required = true, defaultValue = "DEFAULTVALUE")
public void setValue(String value) {
this.value = value;
}
public String getName() {
return name;
}
@XmlTransient
public void setName(String name) {
this.name = name;
}
}
新建JaxbUtils工具类
package com.zhaohy.app.utils;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import com.alibaba.fastjson.JSONObject;
import com.zhaohy.app.entity.SubElement;
import com.zhaohy.app.entity.XmlTestEntity;
public class JaxbUtils {
/**
* Java对象转XML字符串
*
* @param obj 输出对象
* @param xmlHeader xml头部信息 如:"\n"
* @param encoding 编码格式
* @return
*/
public static String object2XmlStr(Object obj, String xmlHeader, String encoding) throws JAXBException {
// 创建输出流
StringWriter sw = new StringWriter();
//sw.write("");
if (xmlHeader != null && xmlHeader.length() > 0) {
sw.write(xmlHeader);
sw.write(System.lineSeparator());
}
// 利用jdk中自带的转换类实现
JAXBContext context = JAXBContext.newInstance(obj.getClass());
Marshaller marshaller = context.createMarshaller();
// 格式化xml输出的格式
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding != null && encoding.length() > 0 ? encoding : "UTF-8");
// 格式化xml输出格式,并去除
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
// 将对象转换成输出流形式的xml
marshaller.marshal(obj, sw);
return sw.toString();
}
/**
* 将String类型的xml转换成Java对象
*
* @param clazz 转换成对应对象class
* @param xmlStr xml字符内容
* @param empty2Null 带有空节点是否需要特殊处理
* @return
*/
@SuppressWarnings("rawtypes")
public static Object xmlStr2Object(Class clazz, String xmlStr, boolean empty2Null) throws JAXBException {
Object xmlObject = null;
JAXBContext context = JAXBContext.newInstance(clazz);
// 进行将Xml转成对象的核心接口
Unmarshaller unmarshaller = context.createUnmarshaller();
if (empty2Null) {
UnmarshallerListener listener = new UnmarshallerListener();
unmarshaller.setListener(listener);
}
UnmarshallerListener listener = new UnmarshallerListener();
unmarshaller.setListener(listener);
StringReader sr = new StringReader(xmlStr);
xmlObject = unmarshaller.unmarshal(sr);
return xmlObject;
}
/**
* 将对象根据路径转换成xml文件
*
* @param obj 输出对象
* @param path 生成xml文件路径
* @param xmlHeader 生成xml的头部信息
* @param encoding 生成xml的编码格式
* @return
*/
public static void object2XmlFile(Object obj, String path, String xmlHeader, String encoding) throws JAXBException, IOException {
// 利用jdk中自带的转换类实现
JAXBContext context = JAXBContext.newInstance(obj.getClass());
Marshaller marshaller = context.createMarshaller();
// 格式化xml输出的格式
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding != null && encoding.length() > 0 ? encoding : "UTF-8");
// 格式化xml输出格式,并去除
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
path = path.replace("\\", "/");
String dir = path.substring(0, path.lastIndexOf("/"));
File file = new File(dir);
if (!file.exists()) {
file.mkdirs();
}
// 将对象转换成输出流形式的xml
try (FileWriter fw = new FileWriter(path)) {
if (xmlHeader != null && xmlHeader.length() > 0) {
//fw.write("");
fw.write(xmlHeader);
fw.write(System.lineSeparator());
}
marshaller.marshal(obj, fw);
} catch (IOException e) {
throw e;
}
}
/**
* 将file类型的xml转换成对象
*/
@SuppressWarnings("unchecked")
public static T xmlFile2Object(Class clazz, String xmlPath, boolean empty2Null) throws JAXBException, IOException {
T xmlObject;
JAXBContext context = JAXBContext.newInstance(clazz);
Unmarshaller unmarshaller = context.createUnmarshaller();
if (empty2Null) {
UnmarshallerListener listener = new UnmarshallerListener();
unmarshaller.setListener(listener);
}
try (FileReader fr = new FileReader(xmlPath)) {
xmlObject =(T) unmarshaller.unmarshal(fr);
} catch (IOException e) {
throw e;
}
return xmlObject;
}
/**
* Java对象转XML字符串
* @param jaxbElement
* @param xmlHeader
* @param properties
* @return
* @throws JAXBException
* @throws IOException
*/
public static String marshal(Object jaxbElement, String xmlHeader) throws JAXBException, IOException {
try (StringWriter writer = new StringWriter()) {
JAXBContext jaxbContext = JAXBContext.newInstance(jaxbElement.getClass());
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
writer.write(xmlHeader);
writer.write(System.lineSeparator());
marshaller.marshal(jaxbElement, writer);
return writer.toString();
}
}
/**
* Java对象转XML字符串
* @param jaxbElement
* @param xmlHeader
* @param properties
* @return
* @throws JAXBException
* @throws IOException
*/
public static String marshal(Object jaxbElement, String xmlHeader, Map properties) throws JAXBException, IOException {
try (StringWriter writer = new StringWriter()) {
JAXBContext jaxbContext = JAXBContext.newInstance(jaxbElement.getClass());
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
for (Entry property : properties.entrySet()) {
marshaller.setProperty(property.getKey(), property.getValue());
}
writer.write(xmlHeader);
writer.write(System.lineSeparator());
marshaller.marshal(jaxbElement, writer);
return writer.toString();
}
}
/**
* XML字符串转Java对象
* @param clazz
* @param xmlStr
* @param empty2Null
* @return
* @throws JAXBException
* @throws IOException
*/
@SuppressWarnings("unchecked")
public static T unmarshal(Class clazz, String xmlStr, boolean empty2Null) throws JAXBException {
JAXBContext context = JAXBContext.newInstance(clazz);
Unmarshaller unmarshaller = context.createUnmarshaller();
if (empty2Null) {
UnmarshallerListener listener = new UnmarshallerListener();
unmarshaller.setListener(listener);
}
UnmarshallerListener listener = new UnmarshallerListener();
unmarshaller.setListener(listener);
StringReader sr = new StringReader(xmlStr);
return (T) unmarshaller.unmarshal(sr);
}
public static void main(String[] args) throws JAXBException {
XmlTestEntity test = new XmlTestEntity();
test.setName("testName");
List list = new ArrayList<>();
SubElement el = new SubElement();
// el.setValue("value");
el.setName("subName");
list.add(el);
el = new SubElement();
el.setValue("value1");
el.setName("subName");
list.add(el);
test.setElements(list);
List list1 = new ArrayList<>();
list1.add("listValue");
list1.add("listValue1");
test.setList(list1);
test.setElementName("xxx");
String xmlStr = JaxbUtils.object2XmlStr(test, "", "UTF-8");
System.out.println(xmlStr);
test = new XmlTestEntity();
test = (XmlTestEntity) JaxbUtils.xmlStr2Object(XmlTestEntity.class, xmlStr, true);
System.out.println(JSONObject.toJSONString(test));
}
}
新建UnmarshallerListener继承Unmarshaller.Listener
package com.zhaohy.app.utils;
import javax.xml.bind.Unmarshaller;
import java.lang.reflect.Field;
/**
* @description: 空节点为空字符串需要转为null时
*/
public class UnmarshallerListener extends Unmarshaller.Listener {
public static final String BLANK_CHAR = "";
@Override
public void afterUnmarshal(Object target, Object parent) {
super.afterUnmarshal(target, parent);
Field[] fields = target.getClass().getDeclaredFields();
for (Field f : fields) {
f.setAccessible(true);
try {
if (f.getType() == String.class && f.get(target) != null&&f.get(target).equals(BLANK_CHAR)) {
f.set(target, null);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
为了处理空节点为空字符串需要转为null的问题
新建LocalDateTimeAdapter继承XmlAdapter
package com.zhaohy.app.common;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class LocalDateTimeAdapter extends XmlAdapter {
@Override
public LocalDateTime unmarshal(String date) throws Exception {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
return LocalDateTime.parse(date, formatter);
}
@Override
public String marshal(LocalDateTime v) throws Exception {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
return formatter.format(v);
}
}
为了处理时间格式化,同@XmlJavaTypeAdapter搭配使用
运行工具类里面的main方法测试,跑出的结果如下:
value1
xxx
listValue
listValue1
2022-05-26 16:08:18
{"dateTime":"2022-05-26T16:08:18","elementName":"xxx","elements":[{"name":"subName"},{"name":"subName","value":"value1"}],"list":["listValue","listValue1"],"name":"testName"}
可以看到对象转xml、xml转对象打印json都已经成功。
注解
可以看到上面实体类里用到了以下这几个注解
@XmlAccessorType
@XmlRootElement
@XmlType
@XmlAttribute
@XmlTransient
@XmlElementWrapper
@XmlElement
@XmlJavaTypeAdapter
这几个注解大概就能应付大部分需求了
@XmlAccessorType
作用于类上,配置一个XmlAccessType枚举类,默认是XmlAccessType.PUBLIC_MEMBER;
jdk里面给这个枚举类的注释如下:
/**
* Every public getter/setter pair and every public field will be
* automatically bound to XML, unless annotated by {@link XmlTransient}.
*
* Fields or getter/setter pairs that are private, protected, or
* defaulted to package-only access are bound to XML only when they are
* explicitly annotated by the appropriate JAXB annotations.
*/
大意是说:对每个public类型的getter方法或setter方法,以及每个public类型的字段会自动绑定到xml,无需特意标识@XmlElement注解绑定,除非标识了@XmlTransient(忽略绑定),其他类型的就需要自己写注解标识绑定才可以
在实体类里我们可以在属性的set方法上再用@XmlElement标识一遍,如果是在字段上标识,因为这种模式下已经在set/get方法上自动绑定一遍了,在字段上再标识相当于重复绑定,所以会抛异常出来。
@XmlRootElement
作用于类上,可以设置根元素的标签名称,比如@XmlRootElement(name = "Test")
@XmlType
作用于类上
用来设置xml标签的顺序,可以把属性按想要的顺序设置到propOrder里,如上面的:@XmlType(propOrder = {"name", "elements", "elementName", "list", "dateTime"})
@XmlAttribute
作用于字段和方法上,用来设置标签的attibute,如@XmlAttribute(name = "Name")
@XmlTransient
作用于类,字段和方法上,忽略绑定到xml.
上面在SubElement类里可以看到name属性上被标识了@XmlAttribute(name = "Name"),因为按照XmlAccessType.PUBLIC_MEMBER模式,public的setter/getter方法会自动把属性绑定到xml,所以在字段上再标识这个注解会报冲突,这时需要在setName方法上添加 @XmlTransient,把自动绑定的那个忽略,就不会重复绑定了。或者在这种模式下@XmlAttribute(name = "Name")不要作用于字段,直接作用于set方法上,这样就不会重复绑定 也就不需要@XmlTransient了。
@XmlElementWrapper
作用于字段和方法上,在元素上设置一个父元素,通常与@XmlElement一起用,比如上面的:
@XmlElementWrapper(name = "Elements")
@XmlElement(name = "Element")
public void setElements(List elements) {
this.elements = elements;
}
@XmlElement
作用于字段和方法上,标识一个xml标签,如:@XmlElement(name = "Element")
@XmlJavaTypeAdapter
作用于包、类、字段,方法、参数上,可以处理时间显示格式,搭配自定义adapter使用,比如上面单独新建的LocalDateTimeAdapter,对LocalDateTime做了显示格式处理:
@XmlJavaTypeAdapter(LocalDateTimeAdapter.class)
public void setDateTime(LocalDateTime dateTime) {
this.dateTime = dateTime;
}
如此便能覆盖到大部分使用场景了,更详细注解讲解可参考如下博文:
参考:https://blog.csdn.net/wn084/article/details/80853587