我的 REST 服务正在接受一个 class A
的对象,该对象具有 objects B
的列表。
所需对象的行为是,如果有一些重复的条目,则需要添加权重。使用jackson
序列化/反序列化
要实现这一点,有两种方法:
- 继承
ArrayList
并覆盖 add 方法并添加逻辑以在存在同名条目时对权重求和。
- 在反序列化 JSON 时使用
@JsonDeserialize
。
首选哪个选项,为什么?
class A {
List<B> objects;
// Getters and Setters
}
class B {
String name;
Float weight;
boolean equals() {
// compares name.
}
}
示例 JSON
{
objects:[{name:"X", weight:10.2},{name:"Y",weight:12.5},{name:"X", weight:20}]
}
这应该产生一个大小为 2 的对象列表,其值为
name:X,weight:30.2 and name:Y,weight:12.5.
To achieve this there are 2 ways around:
- Inherit
ArrayList
and override add method and add logic to sum weights if an entry with same name is present.
- Using
@JsonDeserialize
while deserializing the JSON.
Which option is preferred and why?
顺便说一下,您不应该使用这些方法中的任何一种。见下文:
应避免覆盖 add()
方法来执行计算。您可以利用流来处理已添加到列表中的数据。
Jackson 应该仅用于 JSON 解析,而不用于执行业务逻辑。
事实上,您应该做的是在 Controller 层接收 JSON,将其解析为列表,然后委托(delegate)给服务层,您将在服务层执行分组和求和操作。
此外,重要的是要提到您必须避免Float
和Double
类型的计算,因为它们存在精度问题。请改用 BigDecimal
。
最后,下面是使用 BigDecimal
进行分组和求和的方法:
public List<B> groupByNameAndSumWeight(List<B> list) {
return list.stream()
.collect(groupingBy(B::getName, reducing(BigDecimal.ZERO, B::getWeight, BigDecimal::add)))
.entrySet().stream()
.map(entry -> new B(entry.getKey(), entry.getValue()))
.collect(toList());
}
只需确保您有以下静态导入:
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.reducing;
import static java.util.stream.Collectors.toList;