Java解析/生成xml文件
本文使用SpringBoot环境解析/生成固定格式的xml文件。
1.前提准备
解析xml的方式有多种,这里使用demo4j。
1.1 导入依赖
<dependency> <groupId>org.dom4j</groupId> <artifactId>dom4j</artifactId> <version>2.1.3</version> </dependency>
1.2 文件准备
要解析的xml文件如下:
<?xml version="1.0" encoding="utf-8"?> <Document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="Schema-3.0.xsd" License="00120-100290-002"> <Events> <Event> <School name="武汉大学" establishmentDate="1997-12-20"> <Clazz clazz="计算机101"> <Student name="张三" age="20" sex="男"></Student> <Student name="李敏" age="15" sex="女"></Student> <Student name="赵柳" age="21" sex="女"></Student> <Student name="李民" age="21" sex="男"></Student> </Clazz> <Clazz clazz="计算机102"> <Student name="赵虹" age="22" sex="男"></Student> <Student name="钟菲菲" age="19" sex="女"></Student> <Student name="汪敏" age="20" sex="女"></Student> <Student name="杨倩" age="18" sex="女"></Student> </Clazz> </School> <School name="清华大学" establishmentDate="1988-10-03"> <Clazz clazz="计算机211"> <Student name="赵飞" age="20" sex="男"></Student> <Student name="李慧" age="15" sex="女"></Student> </Clazz> <Clazz clazz="软件311"> <Student name="赵宏宇" age="22" sex="男"></Student> <Student name="汪敏敏" age="21" sex="女"></Student> <Student name="杨明明" age="18" sex="男"></Student> </Clazz> </School> </Event> </Events> </Document>
1.3 创建工具类及对象
1)编写文件工具类FileUtil,用于将MultipartFile转为File
package com.zys.demo.util; import lombok.extern.slf4j.Slf4j; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.net.URLEncoder; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; @Slf4j public class FileUtil { private static final String TEMP_PATH = "D://temp"; /** * MultipartFile 转 File * * @param file * @throws Exception */ public static File multipartFileToFile(MultipartFile file) throws Exception { File toFile = null; if (!file.equals("") && file.getSize() > 0) { InputStream ins = file.getInputStream(); File tempFile = new File(TEMP_PATH); if (!(tempFile.exists() && tempFile.isDirectory())) { tempFile.mkdirs(); } toFile = new File(tempFile + File.separator + file.getOriginalFilename()); inputStreamToFile(ins, toFile); ins.close(); } return toFile; } //获取流文件 private static void inputStreamToFile(InputStream ins, File file) { try { OutputStream os = new FileOutputStream(file); int bytesRead = 0; byte[] buffer = new byte[8192]; while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) { os.write(buffer, 0, bytesRead); } os.close(); ins.close(); } catch (Exception e) { e.printStackTrace(); } } //删除转换存储的临时文件 public static void deleteMultipartFile(MultipartFile file) { File tempFile = new File(TEMP_PATH + File.separator + file.getOriginalFilename()); tempFile.delete(); } }
在进行类型转换时,会生成一个临时文件,默认存在项目根目录下。这里指定了目录,也可在动态传入。
2)创建解析存储的对象:(一个节点就是一个对象,根节点可省)
SchoolModel:学校对象
package com.zys.demo.entity.model; import lombok.Data; import lombok.experimental.Accessors; import java.util.List; @Data @Accessors(chain = true) public class SchoolModel { private String name; private String establishmentDate; private List<ClazzModel> clazzModelList; }
ClazzModel:班级对象
package com.zys.demo.entity.model; import lombok.Data; import lombok.experimental.Accessors; import java.util.List; @Data @Accessors(chain = true) public class ClazzModel { private String clazz; private List<StudentModel> studentModelList; }
StudentModel:学生对象
package com.zys.demo.entity.model; import lombok.Data; import lombok.experimental.Accessors; @Data @Accessors(chain = true) public class StudentModel { private String name; private String sex; private Integer age; }
2.xml解析
1)编写l解析的工具类XmlUtil
package com.zys.demo.util; import com.zys.demo.entity.model.ClazzModel; import com.zys.demo.entity.model.StudentModel; import com.zys.demo.entity.model.SchoolModel; import lombok.extern.slf4j.Slf4j; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import java.io.File; import java.util.ArrayList; import java.util.List; /** * xml文件解析 */ @Slf4j public class XmlUtil { private static xmlUtil xmlUtil = null; public static XmlUtil getInstance() { if (null == xmlUtil) { xmlUtil = new XmlUtil(); } return xmlUtil; } private List<SchoolModel> schoolModelList; private List<ClazzModel> clazzModelList; private List<StudentModel> studentModelList; /** * 解析xml * * @param file * @return */ public List<SchoolModel> parse(File file) { // 创建一个SAXReader对象 SAXReader sax = new SAXReader(); // 获取document对象,如果文档无节点,则会抛出Exception提前结束 Document document; try { schoolModelList = new ArrayList<>(); clazzModelList = new ArrayList<>(); studentModelList = new ArrayList<>(); document = sax.read(file); Element root = document.getRootElement().element("Events").element("Event"); List<Element> elements = root.elements(); elements.stream().forEach(node -> getNodes(node)); } catch (DocumentException e) { e.printStackTrace(); log.error("xml读取异常,原因:文档无节点"); } return schoolModelList; } /** * 递归遍历所有子节点 */ private void getNodes(Element node) { String nodeName = node.getName(); if (nodeName.equals("School")) { SchoolModel school = new SchoolModel(); school.setName(node.attributeValue("name")); school.setEstablishmentDate(node.attributeValue("establishmentDate")); //获取下级节点 List<Element> elementList = node.elements(); elementList.stream().forEach(item -> { this.getNodes(item); }); school.setClazzModelList(clazzModelList); schoolModelList.add(school); //重置列表 clazzModelList = new ArrayList<>(); } else if (nodeName.equals("Clazz")) { ClazzModel clazz = new ClazzModel(); clazz.setClazz(node.attributeValue("clazz")); //获取下级节点 List<Element> elementList = node.elements(); elementList.stream().forEach(item -> { this.getNodes(item); }); clazz.setStudentModelList(studentModelList); clazzModelList.add(clazz); //重置列表 studentModelList = new ArrayList<>(); } else if (nodeName.equals("Student")) { StudentModel student = new StudentModel(); student.setName(node.attributeValue("name")); student.setAge(Integer.parseInt(node.attributeValue("age"))); student.setSex(node.attributeValue("sex")); studentModelList.add(student); } } }
2)编写上传文件的接口(也可以直接使用main方法方式解析)
package com.zys.demo.controller; import com.zys.demo.entity.model.SchoolModel; import com.zys.demo.util.FileUtil; import com.zys.demo.util.XmlUtil; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import java.io.File; import java.util.List; @RestController @RequestMapping("/file") public class FileController { @PostMapping("/import") public List<SchoolModel> importXml(MultipartFile file) throws Exception { //文件转换 File fileToFile = FileUtil.multipartFileToFile(file); //读取内容 List<SchoolModel> modelList = XmlUtil.getInstance().parse(fileToFile); //删除临时文件 FileUtil.deleteMultipartFile(file); return modelList; } }
3)在static下创建上传的页面test.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>xml解析</title> </head> <body> <form action="/file/import" method="post" enctype="multipart/form-data"> <input type="file" name="file" value="选择文件"> <input type="submit" value="上传"> </form> </body> </html>
4)启动项目,上传此xml文件,结果如下:
自此说明已解析成功。
3.xml生成
除了可以解析xml文件外,还可以根据格式生成xml文件。
3.1准备工作
生成文件需要指定文件的节点信息,故需要创建节点对象:
1)Document对象
package com.zys.demo.entity.model; import lombok.Data; import javax.xml.bind.annotation.*; import java.util.ArrayList; import java.util.List; @Data @XmlAccessorType(XmlAccessType.FIELD) @XmlRootElement(name = "Document") public class Document { @XmlAttribute(name = "xmlns:xsi") private String xmlns = "http://www.w3.org/2001/XMLSchema-instance"; @XmlAttribute(name = "xsi:noNamespaceSchemaLocation") private String location = "Schema-3.0.xsd"; @XmlAttribute(name = "License") private String license = "00120-100290-002"; @XmlElement @XmlElementWrapper(name = "Events") private List<Event> Event = new ArrayList<>(); }
部分注解说明如下:
注解 | 说明 |
@XmlAccessorType(XmlAccessType.FIELD) |
让此类中的所有字段都需要映射到XML中。 除XmlAccessType.FIELD外还有其他的几个可配置项进行映射 XmlAccessType.PROPERTY:映射这个类中的属性(get/set方法)到XML XmlAccessType.PUBLIC_MEMBER:将这个类中的所有public的field或property同时映射到XML(默认) XmlAccessType.NONE:不映射 |
@XmlRootElement |
将类映射为xml的根元素。name属性用于指定生成元素的名字,若不指定,默认使用类名小写作为元素名 |
@XmlAttribute |
将字段映射为本类对应的元素。name属性用于指定映射时的节点属性名称,若不指定,默认使用方法名小写作为元素名 |
@XmlElement |
将字段映射为本类对应的子元素。 |
@XmlElementWrapper |
让生成的XML外围被包裹起来。此注解仅允许出现在集合属性上 |
2)Event对象
package com.zys.demo.entity.model; import lombok.Data; 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 java.util.ArrayList; import java.util.List; @Data @XmlAccessorType(XmlAccessType.FIELD) public class Event { @XmlElement private List<School> School; }
3)School对象
package com.zys.demo.entity.model; import lombok.Data; 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 java.util.List; @Data @XmlAccessorType(XmlAccessType.FIELD) public class School { @XmlAttribute(name = "name") private String name; @XmlAttribute(name = "establishmentDate") private String establishmentDate; @XmlElement private List<Class> Clazz; }
4)Clazz对象
package com.zys.demo.entity.model; import lombok.Data; import lombok.experimental.Accessors; 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 java.util.List; @Data @XmlAccessorType(XmlAccessType.FIELD) public class Clazz { @XmlAttribute(name = "clazz") private String clazz; @XmlElement private List<Student> Student; }
5)Student对象
package com.zys.demo.entity.model; import lombok.Data; import lombok.experimental.Accessors; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; @Data @XmlAccessorType(XmlAccessType.FIELD) public class Student { @XmlAttribute(name = "name") private String name; @XmlAttribute(name = "sex") private String sex; @XmlAttribute(name = "age") private Integer age; }
3.2 创建生成文件方法
1)在上述的解析工具类XmlUtil中添加方法
public void createXml(List<School> list, String path) throws JAXBException { //创建存储目录 File file = new File(path); if (!(file.exists() && file.isDirectory())) { file.mkdirs(); } String fileName = UUID.randomUUID() + ".xml"; file = new File(path + File.separator + fileName); //封装节点 com.zys.demo.entity.model.Document document = new com.zys.demo.entity.model.Document(); List<Event> eventList = new ArrayList<>(); Event event = new Event(); event.setSchool(list); eventList.add(event); document.setEvent(eventList); // 将封装的document转为xml JAXBContext context = JAXBContext.newInstance(com.zys.demo.entity.model.Document.class); Marshaller marshaller = context.createMarshaller(); //格式化输入 marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); marshaller.marshal(document, file); }
2)创建接口,用于接收数据并转换为指定的格式
package com.zys.demo.controller; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.zys.demo.entity.model.Clazz; import com.zys.demo.entity.model.School; import com.zys.demo.entity.model.Student; import com.zys.demo.util.XmlUtil; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import javax.xml.bind.JAXBException; import java.util.ArrayList; import java.util.List; @RestController public class TestController { @PostMapping("/create") public String create(@RequestBody JSONArray params) { try { //数据封装为xml需要的格式 List<School> list = new ArrayList<>(); params.stream().forEach(l -> { JSONObject s1 = JSONObject.parseObject(JSONObject.toJSONString(l)); School school = new School(); school.setName(s1.getString("name")); school.setEstablishmentDate(s1.getString("establishmentDate")); List<Clazz> clazzList = new ArrayList<>(); JSONArray clazzArr = s1.getJSONArray("clazzList"); if (clazzArr != null) { clazzArr.stream().forEach(c -> { JSONObject s2 = JSONObject.parseObject(JSONObject.toJSONString(c)); Clazz clazz = new Clazz(); clazz.setClazz(s2.getString("clazz")); List<Student> studentList = new ArrayList<>(); JSONArray studentArr = s2.getJSONArray("studentList"); if (studentArr != null) { studentArr.stream().forEach(s -> { JSONObject s3 = JSONObject.parseObject(JSONObject.toJSONString(s)); Student student = new Student(); student.setName(s3.getString("name")); student.setAge(s3.getInteger("age")); student.setSex(s3.getString("sex")); studentList.add(student); }); } clazz.setStudent(studentList); clazzList.add(clazz); }); } school.setClazz(clazzList); list.add(school); }); XmlUtil.getInstance().createXml(list, "E:\\data"); return "保存成功"; } catch (JAXBException e) { e.printStackTrace(); return "保存失败,出现异常"; } } }
3)使用postman请求测试
请求url(POST):http://localhost:8080/create
请求数据:
[ { "name": "武汉大学", "establishmentDate": "1997-12-20", "clazzList": [ { "clazz": "计算机101", "studentList": [ { "name": "张三", "age": "20", "sex": "男" }, { "name": "李敏", "age": "15", "sex": "女" } ] }, { "clazz": "计算机102 ", "studentList": [ { "name": "赵虹", "age": "22", "sex": "男" }, { "name": "汪敏", "age": "20", "sex": "女" }, { "name": "杨倩", "age": "18", "sex": "女" } ] } ] } ]
在指定路径查看发现文件已在指定路径创建并生成了内容。