接口接收的Json在反序列化的过程中,有时候会遇到一些无法识别的属性(unrecognized properties),通常我们只要在Json处理类库上设置为忽略即可,但有时候作为中间服务,上下游服务希望提供一个透传功能。本文分享Jackson、Fastjson两个Json处理类库下,实现扩展字段收集透传功能。
使用Fastjson类库,核心思路是通过自定义参数解析器来实现
1.自定义注解
/***
* 自定义注解标记对扩展字段的处理
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExtendBody {
}
2.在DTO中定义未识别字段收集属性
public class FastjsonTestDto {
private String name;
private Integer age;
/***
* 用于存储扩展字段
*/
private Map extend = new HashMap<>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Map getExtend() {
return extend;
}
public void setExtend(Map extend) {
this.extend = extend;
}
@Override
public String toString() {
return "FastjsonTestDto{" +
"name='" + name + '\'' +
", age=" + age +
", extend=" + extend +
'}';
}
}
3.自定义参数解析器
/***
* 自定义参数解析器
*/
@Component
public class ExtendJsonResolver implements HandlerMethodArgumentResolver {
private Logger log = LoggerFactory.getLogger(ExtendJsonResolver.class);
/***
* 用于判定是否需要处理该参数分解,返回true为需要,并会去调用下面的方法resolveArgument
* @param methodParameter
* @return
*/
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
return methodParameter.hasParameterAnnotation(ExtendBody.class);
}
/***
* 真正用于处理参数分解的方法,返回的Object就是controller方法上的形参对象
* @param parameter
* @param mavContainer
* @param webRequest
* @param binderFactory
* @return
* @throws Exception
*/
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
try {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
BufferedReader bufferedReader = request.getReader();
StringBuffer json = new StringBuffer();
String lineStr;
while ((lineStr = bufferedReader.readLine()) != null) {
json.append(lineStr);
}
//定义未识别字段处理
ExtraProcessor ep = (object, key, value) -> {
try {
//判断对象类型,将未识别字段写入Map类型的extend属性中
if (object instanceof JacksonTestDto) {
JacksonTestDto dto = (JacksonTestDto) object;
dto.getExtend().put(key, value);
}
} catch (Exception e) {
log.error("unknown properties collect failed,Extra processor error:{}", e.getMessage(), e);
}
};
//将自定义处理实现传入,
JacksonTestDto result = JSON.parseObject(json.toString(), JacksonTestDto.class, ep);
return result;
} catch (JSONException e) {
log.error("unknown properties collect failed,deserialization error:{}", e.getMessage(), e);
throw new BizExcetion(e.getMessage());
} catch (Exception e) {
log.error("unknown properties collect failed, error:{}", e.getMessage(), e);
throw new BizExcetion(e.getMessage());
}
}
}
4.测试接口
@PostMapping("/f")
public void testF(@ExtendBody JacksonTestDto jacksonTestDto) {
System.out.println("fastjson透传测试接收到的请求体==============:" + jacksonTestDto);
}
5.请求body
{
"name":"96",
"age":69,
"fubao":"996"
}
6.测试结果
Jackson中可以直接使用@JsonAnySetter、@JsonAnyGetter两个注解配合使用来实现这个功能
@JsonAnySetter:
1)可用于将非静态、无参数方法定义为“any getter”的标记注释
2)方法返回值必须是Map类型
3)一个实体类中只能用在一个方法上
@JsonAnyGetter:
1)可用以一个将非静态、两个参数(第一个参数是Json中的key,第二参数是json中的value)方法定义为"any setter",
2)使用该注解,将方法注解为从Json内容中找到的无法识别属性的后备处理程序
实现步骤:
1.DTO中增加扩展字段属性,并使用@JsonAnySetter、@JsonAnyGetter注解
public class JacksonTestDto {
private String name;
private Integer age;
private Map extend = new HashMap<>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@JsonAnyGetter
public Map getExtend() {
return extend;
}
@JsonAnySetter
public void setExtend(String key, Object value) {
this.extend.put(key, value);
}
public void setExtend(Map extend) {
this.extend = extend;
}
@Override
public String toString() {
return "JacksonTestDto{" +
"name='" + name + '\'' +
", age=" + age +
", extend=" + extend +
'}';
}
}
2.测试接口:
@PostMapping("/j")
public void testJ(@RequestBody JacksonTestDto jacksonTestDto) {
System.out.println("jackson透传测试接收到的请求体==============:" + jacksonTestDto);
}
3.请求body:
{
"name":"w66",
"age":1,
"describe":"An interesting soul"
}
4.测试结果: