使用 Jackson 和 Spring 将 JavaScript 数组反序列化为 Java LinkedHashSet 不会删除重复项

Posted

技术标签:

【中文标题】使用 Jackson 和 Spring 将 JavaScript 数组反序列化为 Java LinkedHashSet 不会删除重复项【英文标题】:Deserialization of JavaScript array to Java LinkedHashSet using Jackson and Spring doesn't remove duplicates 【发布时间】:2017-05-15 08:53:09 【问题描述】:

假设我有这个客户端 JSON 输入:


   id: "5",
   types: [
      id: "1", types:[],
      id: "2", types:[],
      id: "1", types[]
   ]

我有这门课:

class Entity 
    private String id;
    private Set<Entity> types = new LinkedHashSet<>();

    public String getId() 
        return this.id;
    

    public String setId(String id) 
        this.id = id;
    

    public Set<Entity> getTypes() 
        return types;
    

    @JsonDeserialize(as=LinkedHashSet.class)
    public void setTypes(Set<Entity> types) 
        this.types = types;
    

    @Override
    public boolean equals(Object o)
        if (o == null || !(o instanceof Entity))
            return false;
        
        return this.getId().equals(((Entity)o).getId());
    

我有这个 Java Spring 端点,我在 POST 请求的正文中传递输入:

@RequestMapping(value = "api/entity", method = RequestMethod.POST)
public Entity createEntity(@RequestBody final Entity in) 
    Set<Entity> types = in.getTypes();
    [...]

我想要:

Set&lt;Entity&gt; types = in.getTypes();

只有两个条目的顺序正确...因为其中一个是基于 id 的重复项... 相反,我在 LinkedHashSet (!) 中得到了重复项

我从我的代码中认为删除重复项会自动工作,但显然不是。

这个问题的背景比 Why do I need to override the equals and hashCode methods in Java? 因为它通过 Java Spring 使用隐式 Jackson 序列化。

【问题讨论】:

你的hashCode()函数在哪里? equals(Object)一样是合同的一部分。 谢谢!看来我错过了 - 让我添加它,看看它是否有效 @GurwinderSingh 成功了!!您可以将其添加为答案,如果您愿意,我会接受:) Why do I need to override the equals and hashCode methods in Java?的可能重复 【参考方案1】:

仅覆盖equals 方法将不起作用,因为基于散列的集合同时使用equalshashCode 方法来查看两个对象是否相同。您需要覆盖 Entity 类中的 hashCode() 方法,因为需要正确实现 hashCode()equals() 方法才能使用基于哈希的集合。

如果您的要求是如果Entity 类的两个对象的部分或全部字段相同,那么这两个对象将被视为等效,在这种情况下,您必须同时覆盖@ 987654331@和hashCode()方法。

例如- 如果仅需要 Entity 类中的 id 字段来确定两个对象是否相等,那么您将覆盖 equals(),如下所示:

@Override
public boolean equals(Object o) 
    if (this == o)
        return true;
    if (o instanceof Entity)
        Entity that = (Entity) o;
        return this.id == null ? that.id == null : this.id.equals(that.id);
    
    return false;


但与此同时,hashCode() 方法需要被覆盖,以在 id 具有相同值的情况下生成相同的哈希码,可能是这样的:

@Override
public int hashCode() 
    int h = 17;
    h = h * 31 + id == null ? 0 : id.hashCode();
    return h;

只有现在它才能与基于哈希的集合一起正常工作,因为这两种方法都用于唯一地标识一个对象。


更多信息:

Relationship between hashCode and equals method in Java Why do I need to override the equals and hashCode methods in Java?

【讨论】:

【参考方案2】:

假设如果Entity 类的成员,即idtype 相同,那么Entity 类的对象是完全错误的,除非您覆盖hashcode()equals()明确地发挥作用。

如果您不覆盖 Entity 类中的 hashCode()equals() 函数,那么这两个对象将不同,即使它们的成员中具有相同的数据。

【讨论】:

我忘了hashCode() - 我认为基于哈希的集合使用equals() 来确定它是否是同一个对象.. @MichailMichailidis 您应该始终考虑覆盖hashCode()equals() 而不仅仅是一个。我希望这能解决你的问题:) 是的,我根据 Gurwinder 早些时候的回答弄明白了 :) 谢谢 @MichailMichailidis 干杯! :)【参考方案3】:

在 Java 中,对象相等性是通过覆盖 equals()hashcode() contract 来确定的。

Object中有equals()hashCode()的默认实现。如果您不提供自己的实现,则将使用这些实现。对于equals(),这意味着== 比较:只有当它们是完全相同的对象时,它们才会相等。

回答您的问题LinkedHashSet 中的对象从 Object 类继承 equals()hashcode() 方法的默认行为。覆盖EntityEntity 类的LinkedHashSetequals()hashcode()

请参阅下面的this,了解hashcode()equals() 的默认行为。

【讨论】:

以上是关于使用 Jackson 和 Spring 将 JavaScript 数组反序列化为 Java LinkedHashSet 不会删除重复项的主要内容,如果未能解决你的问题,请参考以下文章

使用 Spring 和 Jackson JSON 将 java.io.Serializable 实例序列化为 JSON [重复]

在 Spring MVC 中,无法使用 Jackson @JsonFormat 将输入绑定到日期字段

Spring Boot 自定义Jackson ObjectMapper

java 将Jackson与Spring Data MongoDB一起使用

无法使用 Jackson 将 java.time.LocalDate 序列化为字符串

Spring Boot没有使用Jackson Kotlin插件