需求描述
简单描述一下需求,用java代码解析xml文件,将xml中的每个节点映射成java对象中的属性,节点值映射成属性值。
XML文件案例
<?xml version="1.0" encoding="UTF-8" ?>
<USER>
<NAME>张三</NAME>
<PHONE>18326395619</PHONE>
<DESC>法外狂徒,张三!</DESC>
</USER>
代码
java对象代码
package com.bai.study_springboot.bean;
import lombok.Data;
import lombok.ToString;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
/**
* author liuBai
* 2023/5/18 9:28
*/
@Data
@ToString
@XmlRootElement(name = "USER")
public class XmlUserBean {
@XmlElement(name = "NAME")
private String name;
@XmlElement(name = "PHONE")
private String phone;
@XmlElement(name = "DESC")
private String desc;
}
转换操作业务代码
package com.bai.study_springboot.bean;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import java.io.*;
public class XmlUserBeanTest {
public static void main(String[] args) {
//文件地址需要根据你的xml文件实际位置,做改变
File file = new File("D:\\project_code\\project_java\\project_my\\study_test_demo\\study-springboot-demo\\study-springboot-demo02\\src\\main\\resources\\user.xml");
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)))) {
/*StringBuffer buffer = new StringBuffer("");
while (reader.ready()) {
buffer.append(reader.readLine());
}*/
JAXBContext jaxbContext = JAXBContext.newInstance(XmlUserBean.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
Object unmarshal = unmarshaller.unmarshal(reader);
System.out.println(unmarshal);
} catch (Exception e) {
e.printStackTrace();
}
}
}
遇到的小坑
报错了,类的两个属性具有相同的名称,java对象的每个属性都会报错,有两个同名属性。
先上解决办法
在java类对象上添加注解@XmlAccessorType(value = XmlAccessType.FIELD)
代码如下:
package com.bai.study_springboot.bean;
import lombok.Data;
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;
/**
* author liuBai
* 2023/5/18 9:28
*/
@Data
@ToString
@XmlAccessorType(value = XmlAccessType.FIELD)
@XmlRootElement(name = "USER")
public class XmlUserBean {
@XmlElement(name = "NAME")
private String name;
@XmlElement(name = "PHONE")
private String phone;
@XmlElement(name = "DESC")
private String desc;
}
分析原有
在分析原因之前,先了解一下@XmlAccessorType注解,@XmlAccessorType注解的源码
@Inherited @Retention(RUNTIME) @Target({PACKAGE, TYPE})
public @interface XmlAccessorType {
/**
* Specifies whether fields or properties are serialized.
*
* @see XmlAccessType
*/
XmlAccessType value() default XmlAccessType.PUBLIC_MEMBER;
}
看源码可以知道,这是一个类级别的注解(注解在类上的注解),该注解的作用是,定义一个类中的何种类型需要映射到XML。解释起来有点拗口,可以通过它的属性值更好理解这个参数的意义。
参数 value
可以接受4个指定值,这几个值是枚举类型,方便调用:
- XmlAccessType.FIELD:映射这个类中的所有字段到XML
- XmlAccessType.PROPERTY:映射这个类中的属性(get/set方法)到XML
- XmlAccessType.PUBLIC_MEMBER:将这个类中的所有public的field或property同时映射到XML(默认)
- XmlAccessType.NONE:不映射
然后在看一段解析XML文件时的源码
当我们的java类没有加@XmlAccessorType 注解的时候,截图中的两个方法都会生效,因为XmlAccessType的value值会是默认PUBLIC_MEMBER。这样就会导致上述的报错了。
当我们添加了注解@XmlAccessorType(value = XmlAccessType.FIELD)时,截图中的只会有第一个方法生效,这样就可以避免上述报错了。
截图中两个方法的源码,这里就不再贴出了!感兴趣的小伙伴,可自行翻看学习
(此处引用了:https://www.w3cschool.cn/jaxb2/jaxb2-q85r2zok.html)@XmlAccessorType注解解释