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": ""
                    }
                ]
            }
        ]
    }
]

在指定路径查看发现文件已在指定路径创建并生成了内容。

posted @ 2021-08-11 22:24  钟小嘿  阅读(3512)  评论(0编辑  收藏  举报