环境
springboot版本2.2.11.RELEASE,jackson version为2.10.0
以下为第一版样例代码,后面XML不会变,ServiceBusReq类的内容会变:
- 请求体
<TX>
<TX_HEADER>
<SYS_HDR_LEN/>
<SYS_PKG_VRSN>01</SYS_PKG_VRSN>-->
</TX_HEADER>
<TX_EMB/>
<ENTITY>
<USER_NAME>xcc</USER_NAME>
<COMPANYS>
<COMPANY>
<COMPANYNAME>NAME-1</COMPANYNAME>
</COMPANY>
<COMPANY>
<COMPANYNAME>NAME-2</COMPANYNAME>
</COMPANY>
</COMPANYS>
</ENTITY>
</TX>
- 实体类
@Data
@JacksonXmlRootElement(localName = "TX")
public class ServiceBusReq<T> {
@JacksonXmlProperty(localName = "TX_HEADER")
private ServiceBusHeaderReq txHeader;
@JacksonXmlProperty(localName = "TX_BODY")
private ServiceBusBodyReq txBody;
@JacksonXmlProperty(localName = "TX_EMB")
private String txEmb;
@JacksonXmlProperty(localName = "ENTITY")
private T msgObject;
}
@Data
@JacksonXmlRootElement(localName = "TX_HEADER")
public class ServiceBusHeaderReq {
@JacksonXmlProperty(localName = "SYS_HDR_LEN")
private String sysHdrLen;
}
@RestController
public class ServiceBusController {
@RequestMapping(method = {RequestMethod.POST},value = "/service",consumes = MediaType.APPLICATION_XML_VALUE)
public ServiceBusResp xmlService(@RequestBody @Valid ServiceBusReq reqDto) throws ClassNotFoundException {
return serviceBusService.invoke(reqDto);
}
}
@Data
@Builder
public class XmlDemoReqDto {
@JacksonXmlProperty(localName = "USER_NAME")
private String UserName;
@JacksonXmlProperty(localName = "COMPANYS")
private List<Company> companys;
}
@Data
@JacksonXmlRootElement(localName = "company")
public class Company {
@JacksonXmlProperty(localName = "COMPANYNAME")
private String companyName;
}
ServiceBusReq采用的泛型,反序列化ENTITY的对象是不固定的。XmlDemoReqDto 就是ServiceBusReq的泛型对象
问题阶段一
controller中reqDto打印的msgObject类型实际上是一个LinkedHashMap,而LinkedHashMap明显不符合序列化后是个java bean的要求
解决方法:
添加一个ObjectNode接收,然后把objectNode再转换成msgObject,代码如下:
@Data
@JacksonXmlRootElement(localName = "TX")
public class ServiceBusReq<T> {
@JacksonXmlProperty(localName = "TX_HEADER")
private ServiceBusHeaderReq txHeader;
@JacksonXmlProperty(localName = "TX_BODY")
private ServiceBusBodyReq txBody;
@JacksonXmlProperty(localName = "TX_EMB")
private String txEmb;
@JacksonXmlProperty(localName = "ENTITY")
private ObjectNode entity;
private T msgObject;
}
/*转换方法*/
private Req convert(ObjectNode entityStr) throws ClassNotFoundException {
Type[] actualTypeArguments = ((ParameterizedTypeImpl) this.getClass().getGenericSuperclass()).getActualTypeArguments();
if (actualTypeArguments.length != 2) {
throw new ServiceException("对象获取异常");
}
Object o = xmlMapper.convertValue(entityStr, Class.forName(actualTypeArguments[0].getTypeName()));
return (Req) o;
}
问题阶段二
以上处理反序列化成对象,但有另一个问题,companys只获取到一个第二个节点,也就是NAME-2的值,NAME-1的值没有获取到
问题阶段三
修改XmlDemoReqDto的定义,代码修改后如下:
@Data
@Builder
public class XmlDemoReqDto {
@JacksonXmlProperty(localName = "USER_NAME")
private String UserName;
// 此处有变更
@JacksonXmlProperty(localName = "COMPANYS")
private Companys companys;
}
// 新增一个Companys的对象,里面包含company list
@Data
public class Companys {
@JacksonXmlProperty(localName = "COMPANY")
private List<Company> companies;
}
@Data
public class Company {
@JacksonXmlProperty(localName = "COMPANYNAME")
private String companyName;
}
按照上面的处理方式,可以正常解析出两个company了
问题阶段四
以上方法在传入的XML中company如果是两个节点,那是正常的,但如果只有一个节点,也就是下面那种,就会出现解析失败的情况,出现异常“java.lang.IllegalArgumentException: Cannot deserialize value of type java.util.ArrayList
<TX>
<TX_HEADER>
<SYS_HDR_LEN/>
<SYS_PKG_VRSN>01</SYS_PKG_VRSN>
</TX_HEADER>
<TX_EMB/>
<ENTITY>
<USER_NAME>xcc</USER_NAME>
<COMPANYS>
<COMPANY>
<COMPANYNAME>NAME-1</COMPANYNAME>
</COMPANY>
</COMPANYS>
</ENTITY>
</TX>
@Data
public class Companys {
@JacksonXmlProperty(localName = "COMPANY")
@JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
private List<Company> companies;
}
处理以上四个阶段问题,消耗了我整整一天的时间,希望能给遇到相同问题的朋友一些解决思路。再次感谢ChatGPT在处理问题中给的一些思路,太有用了。
勉励:未来被人工智能淘汰的不是工程师,而是不会运用人工智能的工程师
参考链接
https://github.com/FasterXML/jackson-databind/issues/2733
https://github.com/FasterXML/jackson-databind/issues/2732
https://github.com/FasterXML/jackson-dataformat-xml/issues/339
👽个人博客:https://everspring.github.io/
👽公众号:爱历史的IT男