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实体类是怎么配置的呢
@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;
}
一上来就看到好多注解,其实前三个不用太关注,只需要看@XmlRootElement
和XmlAccessorType
、@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;
...
}