Spring Boot实现扩展字段功能

  • 前言

接口接收的Json在反序列化的过程中,有时候会遇到一些无法识别的属性(unrecognized properties),通常我们只要在Json处理类库上设置为忽略即可,但有时候作为中间服务,上下游服务希望提供一个透传功能。本文分享Jackson、Fastjson两个Json处理类库下,实现扩展字段收集透传功能。

  • Fastjson扩展字段实现

使用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实现

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.测试结果:

你可能感兴趣的:(springboot,Java,java,spring,boot)