如何设置ObjectMapper默认将JSON数组转为Java Set而非List
问题描述
是否可以设置ObjectMapper的默认行为,将JSON数组转换为Java Set而非List?
更新内容
补充细节:数据是递归结构,JSON中的params字段可能多次出现。Java模型中有对应的Map<String, Object> params字段,该Map的值类型多样,其中一种可能是JSON数组,默认会转为List,目标是切换为Set。
更新内容2(模型与示例)
Java模型代码
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", include = JsonTypeInfo.As.EXISTING_PROPERTY) @JsonSubTypes({ @JsonSubTypes.Type(value = Group.class, name = "group"), @JsonSubTypes.Type(value = Value.class, name = "value") }) public interface Node { String getType(); } public class Group implements Node { List<Node> content; } public class Value implements Node { String name; Map<String, Object> params; }
示例JSON
{ "content": [ { "name": "ID", "params": { "ids": [ "1", "2", "3", "4", "5" ] }, "type": "value" }, { "name": "SUM", "params": { "sum": 400 }, "type": "value" } ], "type": "group" }
当前代码
String content = "json-here"; Node node = objectMapper.readValue(content, Node.class);
更新内容3(可复现最小示例)
import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import java.util.List; import java.util.Map; @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", include = JsonTypeInfo.As.EXISTING_PROPERTY) @JsonSubTypes({ @JsonSubTypes.Type(value = Group.class, name = "group"), @JsonSubTypes.Type(value = Value.class, name = "value") }) interface Node { String getType(); } class Group implements Node { private List<Node> content; public void setContent(List<Node> content) { this.content = content; } @Override public String toString() { return "Group{" + "content=" + content + '}'; } @Override public String getType() { return "group"; } } class Value implements Node { private String name; private Map<String, Object> params; public void setName(String name) { this.name = name; } public void setParams(Map<String, Object> params) { this.params = params; } @Override public String toString() { return "Value{" + "name='" + name + '\'' + ", params=" + params + '}'; } @Override public String getType() { return "value"; } } public class Main { public static void main(String[] args) throws JsonProcessingException { ObjectMapper objectMapper = new ObjectMapper(); String content = """ { "content": [ { "name": "ID", "params": { "ids": [ "1", "2", "3", "4", "5" ] }, "type": "value" }, { "name": "SUM", "params": { "sum": 400 }, "type": "value" } ], "type": "group" } """; Node node = objectMapper.readValue(content, Node.class); System.out.println(node); } }
解决方案
要搞定这个全局把JSON数组转成Set的需求,得自定义TypeModifier来修改Jackson处理Object类型时的数组转换逻辑,同时不影响其他类型的正常序列化/反序列化。
1. 写个自定义TypeModifier
这个类的作用是:当Jackson要把JSON数组转成Object类型时,自动用HashSet代替默认的ArrayList:
import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeModifier; import com.fasterxml.jackson.databind.type.CollectionType; import java.lang.reflect.Type; import java.util.HashSet; import java.util.Set; public class SetAsDefaultCollectionModifier extends TypeModifier { @Override public JavaType modifyType(JavaType type, Type jdkType, com.fasterxml.jackson.databind.type.TypeBindings bindings, com.fasterxml.jackson.databind.type.TypeFactory typeFactory) { // 只要目标类型是Object,就把数组转成HashSet if (type.isJavaLangObject()) { JavaType contentType = typeFactory.constructType(Object.class); return CollectionType.construct(HashSet.class, typeFactory.constructType(Set.class), contentType); } return super.modifyType(type, jdkType, bindings, typeFactory); } }
2. 给ObjectMapper配置这个Modifier
把自定义的Modifier注册到ObjectMapper里,这样全局生效:
ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerModule(new com.fasterxml.jackson.databind.module.SimpleModule() { @Override public void setupModule(com.fasterxml.jackson.databind.module.SetupContext context) { super.setupModule(context); context.addTypeModifier(new SetAsDefaultCollectionModifier()); } });
3. 验证效果
把Main类里的ObjectMapper换成上面配置好的,运行后你会发现params里的ids已经是HashSet类型了,不再是默认的ArrayList。
几点要注意的
- 这个配置是全局的,所有原本会转成
List<Object>的JSON数组,都会变成Set<Object>。 - 如果只是个别字段需要转Set,可以用
@JsonDeserialize(as = HashSet.class)注解,但你要的是全局默认,所以全局配置更合适。 - Set本身会自动去重,如果JSON数组里有重复值,转成Set后会丢失重复项,这点要结合业务场景确认是否能接受。
内容的提问来源于stack exchange,提问作者triclosan
相关产品推荐
相关产品推荐

