在 Spring 应用 RestController 响应中将 null 字符串值写入为空字符串的通用解决方案

问题描述 投票:0回答:1

我有一个带有 React 前端的 Spring 应用程序。前端希望将空字符串值表示为“”而不是null。我需要一种方法来做到这一点,而不必注释项目中的每个字符串字段(并记住注释我的 DTO 中的每个未来字符串值)

我希望我可以注册一个自定义字符串序列化器,如果值为空,它只会输出“”,但当值为空时不会调用 StringValueSerializer。所有空值都直接进入 NullValueSerializer,当您到达 NullValueSerializer 时,无法确定哪个字段具有空值(或者它是什么类型的字段)。

我终于想出了一个解决方案,我将在下面给出答案,因为它需要记录在某处,而我没有更好的地方来放置它。

java spring spring-mvc jackson
1个回答
0
投票

一旦我弄清楚了 NullValueSerializer 问题,我就必须弄清楚如何自定义 SerializerProvider,事实证明这比我在 Spring、Spring-MVC 和 Jackson 的组合中所希望的更具挑战性。

首先我创建了这个新的 SerializerProvider 实现。它重写了 findNullValueSerializer,在这里我们可以查看正在序列化的属性类型,而不仅仅是值。它只是查看属性类型是否为 String,如果是,则给出一个仅写入“”的序列化器,否则它让常规 NullValueSerializer 工作。这意味着任何非字符串(Long、Date、Object、Collection)仍将具有输出 null、{} 或 [] 而不是“”的“正确”行为。我在其他一些解决方案中看到的问题是它们会为空集合输出“”。

package com.mycompany.internal;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.ser.DefaultSerializerProvider;
import com.fasterxml.jackson.databind.ser.SerializerFactory;

import java.io.IOException;

/**
 * Customization to write "" instead of 'null' when a string value is null in our JSON response to the front end.
 */
public class NullStringFriendlySerializerProvider extends DefaultSerializerProvider {

    public NullStringFriendlySerializerProvider(DefaultSerializerProvider provider) {
        super(provider);
    }

    public NullStringFriendlySerializerProvider(SerializerProvider src, SerializationConfig config, SerializerFactory factory) {
        super(src, config, factory);
    }

    /**
     * Magic that writes an empty string instead of null for null string values.
     */
    JsonSerializer<Object> nullAsEmptyStringSerializer = new JsonSerializer<>() {
        @Override
        public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            jsonGenerator.writeString("");
        }
    };

    /**
     * The really real magic that only uses the null string serializer if the field is of type string.
     *
     * @param property the property (not the value) that is being serialized
     * @return the serializer to use for the property.
     * @throws JsonMappingException if bad things happen
     */
    @Override
    public JsonSerializer<Object> findNullValueSerializer(BeanProperty property) throws JsonMappingException {
        if (String.class.equals(property.getType().getRawClass())) {
            return nullAsEmptyStringSerializer;
        } else {
            return super.findNullValueSerializer(property);
        }
    }

    /**
     * Needed for the abstract implementation
     *
     * @param serializationConfig dunno
     * @param serializerFactory   dunno
     * @return our singleton instance that delegates for everything else.
     */
    @Override
    public DefaultSerializerProvider createInstance(SerializationConfig serializationConfig, SerializerFactory serializerFactory) {
        return new NullStringFriendlySerializerProvider(this, serializationConfig, serializerFactory);
    }
}

一旦你有了这个类,你需要在 ObjectMapper 上设置它。我对网上提到的其他选项没有任何运气,但我确实在我的 Spring Boot 应用程序中得到了这个工作。它是一个允许您自定义 ObjectMapper 的钩子。

@Configuration
public static class JacksonConfig {
    
    /**
     * We need to configure with this implementation so that we write a null value in a string field as empty string instead of null.
     *
     * @return the builder customizer.
     */
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer customizeObjectMapper() {
        return builder -> builder.postConfigurer(mapper -> {
                    mapper.setSerializerProvider(new NullStringFriendlySerializerProvider((DefaultSerializerProvider) mapper.getSerializerProvider()));
                }
        );
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.