简介
本文介绍使用Spring自带的Jackson转换XML与Java对象的方法。
XML与Java对象转换的方法有很多,最好的方法是使用Spring自带的XmlMapper,本文介绍此工具的用法。
XmlMapper类是 ObjectMapper类的子类,两者使用方式几乎一致。
依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> <!-- 版本由spring-boot-starter-parent指定--> </dependency>
jackson-dataformat-xml依赖了jackson-core、jackson-databind、jackson-annotations等,这些都在spring-boot-starter-web中引入了。
整个pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.4.13</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.knife</groupId> <artifactId>Demo_XML_SpringBoot</artifactId> <version>0.0.1-SNAPSHOT</version> <name>Demo_XML_SpringBoot</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> </dependency> <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-spring-boot-starter</artifactId> <version>3.0.3</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.24</version> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
工具类
package com.knife.example.common.util; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.deser.std.DateDeserializers; import com.fasterxml.jackson.databind.ser.std.DateSerializer; import com.fasterxml.jackson.dataformat.xml.XmlMapper; import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer; import lombok.SneakyThrows; import java.text.SimpleDateFormat; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.format.DateTimeFormatter; import java.util.Date; public class XmlUtil { private static final XmlMapper xmlMapper = createXmlMapper(); private static XmlMapper createXmlMapper() { XmlMapper xmlMapper = XmlMapper.builder() // // 忽略实体类没有对应属性。如果为 true 会抛出异常 // .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false) // // 忽略null // .serializationInclusion(JsonInclude.Include.NON_NULL) // // 属性使用 驼峰首字母小写 // .propertyNamingStrategy(PropertyNamingStrategy.LOWER_CAMEL_CASE) // .defaultUseWrapper(false) .build(); // 序列化时加上文件头信息 xmlMapper.configure(ToXmlGenerator.Feature.WRITE_XML_DECLARATION, true); // 美化输出结果(给XML添加缩进) xmlMapper.enable(SerializationFeature.INDENT_OUTPUT); // 把“忽略重复的模块注册”禁用,否则下面的注册不生效 xmlMapper.disable(MapperFeature.IGNORE_DUPLICATE_MODULE_REGISTRATIONS); xmlMapper.registerModule(configTimeModule()); // 重新设置为生效,避免被其他地方覆盖 xmlMapper.enable(MapperFeature.IGNORE_DUPLICATE_MODULE_REGISTRATIONS); return xmlMapper; } private static JavaTimeModule configTimeModule() { JavaTimeModule javaTimeModule = new JavaTimeModule(); String localDateTimeFormat = "yyyy-MM-dd HH:mm:ss"; String localDateFormat = "yyyy-MM-dd"; String localTimeFormat = "HH:mm:ss"; String dateFormat = "yyyy-MM-dd HH:mm:ss"; // 序列化 javaTimeModule.addSerializer( LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(localDateTimeFormat))); javaTimeModule.addSerializer( LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(localDateFormat))); javaTimeModule.addSerializer( LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(localTimeFormat))); javaTimeModule.addSerializer( Date.class, new DateSerializer(false, new SimpleDateFormat(dateFormat))); // 反序列化 javaTimeModule.addDeserializer( LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(localDateTimeFormat))); javaTimeModule.addDeserializer( LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(localDateFormat))); javaTimeModule.addDeserializer( LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(localTimeFormat))); javaTimeModule.addDeserializer(Date.class, new DateDeserializers.DateDeserializer(){ @SneakyThrows @Override public Date deserialize(JsonParser jsonParser, DeserializationContext dc){ String text = jsonParser.getText().trim(); SimpleDateFormat sdf = new SimpleDateFormat(dateFormat); return sdf.parse(text); } }); return javaTimeModule; } public static String toXml(Object o) { String xml = null; try { xml = xmlMapper.writeValueAsString(o); } catch (JsonProcessingException e) { throw new RuntimeException(e); } return xml; } public static <T> T toObject(String xml, Class<T> cls) { T t = null; try { t = xmlMapper.readValue(xml, cls); } catch (JsonProcessingException e) { throw new RuntimeException(e); } return t; } }
测试类
package com.knife.example.business.controller; import com.knife.example.business.bo.SchoolBO; import com.knife.example.business.bo.StudentBO; import com.knife.example.common.util.XmlUtil; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; @Api(tags = "测试XML") @RestController @RequestMapping("xml") public class TestController { @ApiOperation("对象转XML") @PostMapping("toXmlString") public String toXmlString() { SchoolBO schoolBO = new SchoolBO(); schoolBO.setSchoolId(222); schoolBO.setSchoolName("ABC中学"); schoolBO.setSchoolEmail("aa@163.com"); schoolBO.setSchoolAddress("A省B市"); List<StudentBO> studentBOList = new ArrayList<>(); StudentBO studentBO1 = new StudentBO(); studentBO1.setStudentName("Tony"); studentBO1.setCreateTime(LocalDateTime.now()); StudentBO studentBO2 = new StudentBO(); studentBO2.setStudentName("Peter"); studentBO2.setCreateTime(LocalDateTime.now().plusSeconds(2)); studentBOList.add(studentBO1); studentBOList.add(studentBO2); schoolBO.setStudentBOList(studentBOList); return XmlUtil.toXml(schoolBO); } @ApiOperation("XML转对象") @PostMapping("toObject") public Object toObject(String xml) { return XmlUtil.toObject(xml, SchoolBO.class); } }
实体类
- @JacksonXmlRootElement:指定生成xml根标签的名字
- 属性:namespace,localName
- @JacksonXmlProperty:指定包装标签名,或者指定标签内部属性名
- 属性:namespace,localName,isAttribute(default:false)
- @JacksonXmlElementWrapper:可用于List等集合类,用来指定外围标签名
- 属性:namespace,localName,useWrapping (default:true)
- @JacksonXmlText:指定当前这个值,没有xml标签包裹。
- 属性:namespace,localName
顶层实体类
package com.knife.example.business.bo; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; import lombok.Data; import java.util.List; @Data @JacksonXmlRootElement(localName = "school") public class SchoolBO { // 可以指定XML标签的名字。若不指定,则为字段名 @JacksonXmlProperty(localName = "SchoolName") private String schoolName; // isAttribute 设为true时,该字段做为根标签的属性 @JacksonXmlProperty(isAttribute = true) private Integer schoolId; private String schoolEmail; private String schoolAddress; // 指定外层的XML标签名 @JacksonXmlElementWrapper(localName = "students") // 指定单个元素的XML标签名 @JacksonXmlProperty(localName = "item") private List<StudentBO> studentBOList; }
子层实体类
package com.knife.example.business.bo; import lombok.Data; import java.time.LocalDateTime; @Data public class StudentBO { private String studentName; private LocalDateTime createTime; }
测试
1.对象转XML
2.XML转对象
使用此XML测试:
<?xml version='1.0' encoding='UTF-8'?> <school schoolId="222"> <schoolEmail>aa@163.com</schoolEmail> <schoolAddress>A省B市</schoolAddress> <SchoolName>ABC中学</SchoolName> <students> <item> <studentName>Tony</studentName> <createTime>2023-03-06 19:46:42</createTime> </item> <item> <studentName>Peter</studentName> <createTime>2023-03-06 19:46:44</createTime> </item> </students> </school>
结果:
请先
!