我试图了解如何通过配置/依赖项更改来解决这个特定问题。
Java版本:1.8
Springboot版本:2.7.17
请求和响应模型架构是从 XSD 架构生成的。
我尝试用一个小项目进行调试并寻找一些解决方案
发生了什么:
当我尝试 XML 有效负载时,由于
@XmlElement
名称属性,请求和响应模型符合预期,但是当尝试使用 JSON 有效负载时,它尊重类字段而不是 @XmlElement
名称属性。
寻找一些解决方案来解决这个特定问题,其中我将不被允许更改 XSD 的定义,但愿意通过某种配置更改来更改解析逻辑。
限制:我们的 XSD 文件已维护多年,POJO 类将使用
cxf-codegen-plugin
版本 3.0.3
和目标 wsdl2java
生成。因此我不会获得更改 POJO 模式的许可。礼貌地要求提供向后兼容的解决方案。
这是更容易调试的等效示例项目:
XML 有效负载:
POST http://localhost:9990/api/v1/users/test
Content-Type: application/xml
Accept: application/json
<InputRequest>
<ACCOUNT_NO>12</ACCOUNT_NO>
<FLAGS>2</FLAGS>
<POID>1.0.0</POID>
<PRODUCTS elem="0">
<CODE>1400337</CODE>
</PRODUCTS>
</InputRequest>
JSON 负载:
POST http://localhost:9990/api/v1/users/test
Content-Type: application/json
Accept: application/json
{
"ACCOUNT_NO": "122",
"FLAGS": 2,
"POID": "1.0.0",
"PRODUCTS": {
"CODE": "1222",
"ELEM": 0
}
}
预期回应:
{
"ACCOUNT_NO": "122",
"FLAGS": 2,
"POID": "1.0.0",
"PRODUCTS": {
"CODE": "1222",
"ELEM": 0
}
}
现实:
{
"accountno": null,
"flags": 0,
"poid": null,
"products": []
}
POJO(从 XSD 生成):
package com.example.demo;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.*;
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(
name = "",
propOrder = {"accountno", "flags", "poid", "products"}
)
@XmlRootElement(
name = "InputRequest"
)
public class InputRequest implements Serializable {
private static final long serialVersionUID = 1L;
@XmlElement(
name = "ACCOUNT_NO",
required = true
)
protected String accountno;
@XmlElement(
name = "FLAGS"
)
protected int flags;
@XmlElement(
name = "POID",
required = true
)
protected String poid;
@XmlElement(
name = "PRODUCTS",
required = true
)
protected List<PRODUCTS> products;
public InputRequest() {
}
public String getACCOUNTNO() {
return this.accountno;
}
public void setACCOUNTNO(String value) {
this.accountno = value;
}
public int getFLAGS() {
return this.flags;
}
public void setFLAGS(int value) {
this.flags = value;
}
public String getPOID() {
return this.poid;
}
public void setPOID(String value) {
this.poid = value;
}
public List<PRODUCTS> getPRODUCTS() {
if (this.products == null) {
this.products = new ArrayList();
}
return this.products;
}
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(
name = ""
)
public static class PRODUCTS extends CODE implements Serializable {
private static final long serialVersionUID = 1L;
@XmlAttribute(
name = "elem"
)
protected String elem;
public PRODUCTS() {
}
public String getElem() {
return this.elem;
}
public void setElem(String value) {
this.elem = value;
}
}
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(
propOrder = {"code"}
)
public static class CODE implements Serializable {
private static final long serialVersionUID = 1L;
@XmlElement(
name = "CODE",
required = true
)
protected String code;
public CODE() {
}
public String getCODE() {
return this.code;
}
public void setCODE(String value) {
this.code = value;
}
}
}
休息控制器:
@RestController
@RequestMapping("/api/v1/users")
public class TestController {
@ResponseStatus(HttpStatus.CREATED)
@PostMapping(consumes = {MediaType.APPLICATION_JSON_VALUE,MediaType.APPLICATION_XML_VALUE},value = "/test")
public InputRequest echoXmlUser(@RequestBody InputRequest payload) throws IOException {
System.out.println(new ObjectMapper().writeValueAsString(payload));
return payload;
}
}
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.7.17</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- <dependency>-->
<!-- <groupId>com.fasterxml.jackson.dataformat</groupId>-->
<!-- <artifactId>jackson-dataformat-xml</artifactId>-->
<!-- <version>2.4.0</version>-->
<!-- </dependency>-->
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.8</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<builder>paketobuildpacks/builder-jammy-base:latest</builder>
</image>
</configuration>
</plugin>
</plugins>
</build>
</project>
我尝试使用
jackson-dataformat-xml
和 jaxb-runtime
库探索一些选项,并尝试调整配置以使其工作。
我正在寻找一些选项,其中该 POJO 保持不变(由于更改后不同客户端的更广泛影响的限制)并且还通过配置更改/依赖项更改支持 XML 和 JSON 有效负载。我也不想在控制器内部做太多改变,因为如果要做这样的事情,我也需要改变 50 个这样不同的控制器。
您将需要使用 JAXB 模块 来允许 Jackson 将 JAXB 注释用于元数据。添加为依赖项后,您需要添加一个
@Bean
方法来公开 JaxbAnnotationModule
,以便 Spring Boot 拾取它并添加到 Jackson。
@Bean
public JaxbAnnotationModule jaxbAnnotationModule() {
return new JaxbAnnotationModule();
}
依赖性
<dependency>
<groupId>tools.jackson.module</groupId>
<artifactId>jackson-module-jaxb-annotations</artifactId>
</dependency>
我怀疑 Spring Boot 会为您管理
jackson-module-jaxb-annotations
的版本(通过包含的 jackson-bom
)。如果没有,您应该能够使用 ${jackson.version}
作为 <version>
,因为它将包含用于您的 Spring Boot 版本的 Jackson 版本。