优雅的自定义 spring 返回json 中 null 的变动

468 阅读1分钟

一般而言,如果后端返回的属性值为 null,最好的处理办法就是连属性名也删掉,因为 iOS 没有 null,前端字典遍历有值但又展示 null,是比较尴尬的时候,然后万事都说不准,这不,PHP 又来捣乱了,要求属性值为 null 也要展示属性名,同时还要求 string 的 null 用 "" 代替。

spring 中 json 工具类是 JackSon,以此为例。

1. 自定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GamePHPBody {
}

2. 核心类

static class PHPMessageConverter extends MappingJackson2HttpMessageConverter {

    /**
     * 处理数组类型的null值
     */
    public class NullArrayJsonSerializer extends JsonSerializer<Object> {

        @Override
        public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
            if (value == null) {
                jgen.writeStartArray();
                jgen.writeEndArray();
            }
        }
    }


    /**
     * 处理字符串类型的null值
     */
    public class NullStringJsonSerializer extends JsonSerializer<Object> {

        @Override
        public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
            jsonGenerator.writeString(StringUtils.EMPTY);
        }
    }

    /**
     * 处理数字类型的null值
     */
    public class NullNumberJsonSerializer extends JsonSerializer<Object> {

        @Override
        public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
            jsonGenerator.writeNumber(0);
        }
    }

    /**
     * 处理布尔类型的null值
     */
    public class NullBooleanJsonSerializer extends JsonSerializer<Object> {

        @Override
        public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
            jsonGenerator.writeBoolean(false);
        }
    }


    public class MyBeanSerializerModifier extends BeanSerializerModifier {


        @Override
        public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
            //循环所有的beanPropertyWriter
            for (Object beanProperty : beanProperties) {
                BeanPropertyWriter writer = (BeanPropertyWriter) beanProperty;
                //判断字段的类型,如果是array,list,set则注册nullSerializer
                if (isArrayType(writer)) {
                    //给writer注册一个自己的nullSerializer
                    writer.assignNullSerializer(new NullArrayJsonSerializer());
//                } else if (isNumberType(writer)) {
//                    writer.assignNullSerializer(new NullNumberJsonSerializer());
//                } else if (isBooleanType(writer)) {
//                    writer.assignNullSerializer(new NullBooleanJsonSerializer());
                } else if (isStringType(writer)) {
                    writer.assignNullSerializer(new NullStringJsonSerializer());
                }
            }
            return beanProperties;
        }

        /**
         * 是否是数组
         */
        private boolean isArrayType(BeanPropertyWriter writer) {
            Class<?> clazz = writer.getType().getRawClass();
            return clazz.isArray() || Collection.class.isAssignableFrom(clazz);
        }

        /**
         * 是否是string
         */
        private boolean isStringType(BeanPropertyWriter writer) {
            Class<?> clazz = writer.getType().getRawClass();
            return CharSequence.class.isAssignableFrom(clazz) || Character.class.isAssignableFrom(clazz);
        }


        /**
         * 是否是int
         */
        private boolean isNumberType(BeanPropertyWriter writer) {
            Class<?> clazz = writer.getType().getRawClass();
            return Number.class.isAssignableFrom(clazz);
        }

        /**
         * 是否是boolean
         */
        private boolean isBooleanType(BeanPropertyWriter writer) {
            Class<?> clazz = writer.getType().getRawClass();
            return clazz.equals(Boolean.class);
        }

    }

    PHPMessageConverter() {
        getObjectMapper().setSerializerFactory(getObjectMapper().getSerializerFactory().withSerializerModifier(new MyBeanSerializerModifier()));
    }

}

3. 加入 spring 配置

public class GamePHPMethodProcessor implements HandlerMethodReturnValueHandler {

    private GameFormMethodArgumentResolver formResolver;
    private GameJsonMethodArgumentResolver jsonResolver;

    public GamePHPMethodProcessor() {
        List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
        PHPMessageConverter PHPMessageConverter = new PHPMessageConverter();
        messageConverters.add(PHPMessageConverter);

        jsonResolver = new GameJsonMethodArgumentResolver(messageConverters);
        formResolver = new GameFormMethodArgumentResolver();
    }

    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        return returnType.hasMethodAnnotation(GamePHPBody.class);
    }

    @Override
    public void handleReturnValue(Object o, MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest) throws Exception {
        jsonResolver.handleReturnValue(o, methodParameter, modelAndViewContainer, nativeWebRequest);
    }
}

    public static class MyMvcConfigurer implements WebMvcConfigurer {
        public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers){
            handlers.add(new GamePHPMethodProcessor());
        }

}