自定义Set集合序列化/反序列化器:空值与空集合互转需求
嘿,我懂你现在的困扰——之前处理字符串空值转null的方法用到了asText(),但集合这块JsonNode没提供对应的asSet(),确实得换个思路。刚好我之前也处理过类似的需求,给你分享一下可行的实现方案:
我们需要分别实现序列化器(把null Set转成空Set)和反序列化器(把空Set转成null),借助Jackson的标准扩展类来实现,同时复用它的默认序列化逻辑,不用自己手动解析集合元素。
一、反序列化器:空Set → null
核心逻辑:读取JSON节点后,判断是否为空数组(对应空Set),如果是直接返回null;否则交给Jackson默认逻辑反序列化成带元素的Set。这里用convertValue来把JsonNode转成Set,避免手动处理泛型和元素解析。
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import java.io.IOException; import java.util.Set; @JsonComponent public class EmptySetToNullDeserializer extends StdDeserializer<Set<?>> { public EmptySetToNullDeserializer() { super(Set.class); } @Override public Set<?> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { JsonNode node = p.readValueAsTree(); // 节点为null直接返回null if (node == null) { return null; } // 空数组对应空Set,返回null if (node.isArray() && node.size() == 0) { return null; } // 复用Jackson默认逻辑反序列化为Set return ctxt.getObjectMapper().convertValue(node, new TypeReference<Set<?>>() {}); } }
二、序列化器:null Set → 空Set
序列化逻辑更简单:当Set为null时,序列化为空数组;否则正常输出集合内容。
import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import java.io.IOException; import java.util.Collections; import java.util.Set; @JsonComponent public class NullSetToEmptySerializer extends JsonSerializer<Set<?>> { @Override public void serialize(Set<?> set, JsonGenerator gen, SerializerProvider serializers) throws IOException { // null Set转成空Set序列化 if (set == null) { gen.writeObject(Collections.emptySet()); } else { // 有元素时正常序列化 gen.writeObject(set); } } }
三、使用方式
全局生效
如果是Spring Boot项目,给两个类加上@JsonComponent注解后,Spring会自动把它们注册到Jackson的ObjectMapper中,全局所有Set类型都会应用这个规则。
局部生效
如果只想给特定字段用,在字段上单独指定序列化器/反序列化器:
public class DemoEntity { @JsonSerialize(using = NullSetToEmptySerializer.class) @JsonDeserialize(using = EmptySetToNullDeserializer.class) private Set<String> tags; // getter、setter省略 }
为什么你之前的思路不适用?
你之前尝试用node.asSet(),但JsonNode确实没有这个方法——JSON里的集合对应数组节点,不是简单的文本节点,所以不能用处理字符串的方式。用convertValue可以让Jackson帮我们完成数组到Set的转换,同时自动处理集合内元素的序列化/反序列化,不用自己遍历节点拼接集合。
如果需要处理特定泛型的Set(比如Set<User>),可以把序列化器/反序列化器的泛型改成Set<User>,这样能更精准,但上面的Set<?>版本是全局通用的。
内容的提问来源于stack exchange,提问作者Occams Stubble

