xml文件数据转Java对象

缘起

最近公司中有个项目涉及到导入xml文件且将这些数据实行处理后入库操作,本篇文章介绍下如何实现。

通过在网上查找资料,大概了解了有几种方式可以实现:

  • 在接收xml数据的Java实体类上使用注解配合Unmarshaller来实现
  • 将xml转为json(如果涉及到后续要操作入库,个人觉得比较复杂了),xml转json方式也挺多,可自行搜索

个人觉得为了减少程序中的硬编码过多问题,尽量使用注解的方式,采用声明式解析xml数据,而且这些注解还是java依赖库中自带的,使用起来也很方便。

当然唯一的缺点就是如过标签元素过多,那么可能需要创建很多的Java实体类进行接收。

实现

依赖,其实就是通常的springboot项目依赖,版本自己选择一个即可

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

xml文件,为了简要说明,只截取了一部分数据。

<OdfBody CompetitionCode="xxx" DocumentCode="xxx" DocumentType="xxx" Version="8" ResultStatus="LIVE" FeedFlag="P" Date="2022-09-02" Time="075214560" LogicalDate="2022-09-02" Source="xxxx">
    <Competition>
        <Result SortOrder="1" StartOrder="1" StartSortOrder="1">
            ...
        </Result>
        <Result SortOrder="2" StartOrder="2" StartSortOrder="2">
            ...
        </Result>
        <Result SortOrder="3" StartOrder="3" StartSortOrder="3">
            ...
        </Result>
    </Competition>
</OdfBody>

Controller作为导入xml文件的入口

@RestController
@RequestMapping("/business/xml")
@AllArgsConstructor
public class BusinessXmlController {

    private final BusinessXmlServiceImpl xmlService;

    @PostMapping("/import")
    public JsonResult importXml(@RequestParam("file")MultipartFile file) {
        return xmlService.importXml(file);
    }

}

BusinessXmlServiceImpl

业务层主要通过字符缓冲流配合Unmarshaller来实现

@Service
public class BusinessXmlServiceImpl {

    public JsonResult importXml(MultipartFile file) {
        // 用字符缓冲流获取XML文件内容
        StringBuffer buffer;
        try (BufferedReader br = new BufferedReader(new InputStreamReader(file.getInputStream(), StandardCharsets.UTF_8))) {
            buffer = new StringBuffer();
            String line;
            while ((line = br.readLine()) != null) {
                buffer.append(line);
            }
            // XML转为JAVA对象
            OdfBody odfBody = (OdfBody) XmlBuilder.xmlStrToObject(OdfBody.class, buffer.toString());
            System.out.println(JSON.toJSONString(odfBody));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return JsonResult.SUCCESS;
    }

}

重点就在接收数据的Java实体类上了,上面是解析为OdfBody的对象,其实也就是xml文件中的最顶级的父节点元素,图来

java与xml数据互转 xml转java对象_java与xml数据互转

那么Java实体类是怎么配置的呢

@Data
@AllArgsConstructor
@NoArgsConstructor
@XmlRootElement(name = "OdfBody")
@XmlAccessorType(XmlAccessType.FIELD)
public class OdfBody implements Serializable {

    @XmlAttribute(name = "CompetitionCode")
    private String competitionCode;

    @XmlElement(name = "Competition")
    private Competition competition;

}

一上来就看到好多注解,其实前三个不用太关注,只需要看@XmlRootElementXmlAccessorType@XmlAttribute@XmlElement这几个就行了。

  • @XmlRootElement:声明当前类对应的xml标签元素名称,如@XmlRootElement(name = "OdfBody"),那么代表当前类对应的xml标签名是OdfBody,也就是上图中的最外层元素。
  • @XmlAccessorType:@XmlAccessorType(XmlAccessType.FIELD)声明访问的类型是字段
  • @XmlAttribute:声明在当前标签中的属性名,OdfBody类中的@XmlAttribute(name = "CompetitionCode")也就是在上图的<OdfBody>标签中找属性名是CompetitionCode的属性
  • @XmlElement:可以理解为子元素标签,OdfBody类中的@XmlElement(name = "Competition"),也就是上图中<OdfBody>标签中找名称是Competition的子标签。

以上了解完毕对基本使用应该是没啥问题了,如过涉及到标签名相同的怎么接收呢?

当然是用Java集合进行接收了。

按照上图继续往下看,在<Competition>标签中有很多的<Result>标签,该怎么接收呢?

@Data
@AllArgsConstructor
@NoArgsConstructor
@XmlRootElement(name = "Competition")
@XmlAccessorType(XmlAccessType.FIELD)
public class Competition implements Serializable {

    @XmlElement(name = "ExtendedInfos")
    private ExtendedInfos extendedInfos;

    @XmlElement(name = "Officials")
    private Officials officials;

    @XmlElement(name = "Result")
    private List<Result> resultList;

}

同样的,先把当前类和相应的xml标签对应起来(@XmlRootElement(name = “Competition”)),然后再确定自己要这个xml中的某些属性(用@XmlAttribute),或者想继续找当前xml标签的子xml标签(用@XmlElement)。

当有很多的相同名称的xml标签时,像上面使用List集合接收即可。

最后再贴下上个代码块中的Reulst

@Data
@NoArgsConstructor
@AllArgsConstructor
@XmlRootElement(name = "Result")
@XmlAccessorType(XmlAccessType.FIELD)
public class Result implements Serializable {

    @XmlAttribute(name = "StartOrder")
    private Integer startOrder;

    ...

}