我希望基于this article (Why objects must be immutable)使我的对象不可变。
但是,我正在尝试使用Jackson Object Mapper解析对象。我最初是JsonMappingException: No suitable constructor found for type [simple type, class ]: cannot instantiate from JSON object.
我可以按照here的说明进行修复,方法是提供一个默认的构造函数并使我的字段为非最终字段。
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;
@AllArgsConstructor
// @NoArgsConstructor(access = AccessLevel.PRIVATE)
@Builder
@Data
public class School {
@NonNull
private final String schoolId;
@NonNull
private final String schoolName;
}
为克服该问题,我应该遵循什么良好的编程风格?唯一的解决方法是使我的对象可变吗?
我可以使用不使用默认构造函数的其他映射器吗?
答案 0 :(得分:6)
您可以使用Jackson工厂(用@JsonCreator注释的方法)从地图上读取字段并调用非默认构造函数:
class School {
//fields
public School(String id, String name) {
this.schoolId = id;
this.schoolName = name;
}
@JsonCreator
public static School create(Map<String, Object> object) {
return new School((String) object.get("schoolId"),
(String) object.get("schoolName"));
}
//getters
}
Jackson将使用create
版本的json调用Map
方法。这有效地解决了这个问题。
我相信您的问题是在寻找一种Jackson解决方案,而不是一种新的模式/风格。
答案 1 :(得分:1)
有一个用@JsonCreator
注释的静态工厂方法的imo更好的替代方法,该方法具有所有元素的构造函数(无论如何,不可变类都是必需的)。用@JsonCreator
注释 that ,还用@JsonProperty
注释所有参数,如下所示:
class School {
//fields
@JsonCreator
public School(
@JsonProperty("id") String id,
@JsonProperty("name") String name) {
this.schoolId = id;
this.schoolName = name;
}
//getters
}
@JsonCreator
注释为您提供了这些选项。它在其文档中这样描述它们:
- 不带JsonProperty注释的单参数构造函数/工厂方法:如果是这样,则这就是所谓的“委托创建者”,在这种情况下,Jackson首先将JSON绑定到参数的类型中,然后调用creator。这通常与JsonValue(用于序列化)结合使用。
- 构造器/工厂方法,其中每个参数均用JsonProperty或JacksonInject注释,以指示要绑定的属性的名称
在某些情况下,甚至可能不需要显式指定参数名称。有关@JsonCreator
的文档进一步指出:
还请注意,除非您使用可以检测参数名称的扩展模块之一,否则所有JsonProperty批注都必须指定实际名称(“ default”不为空字符串)。这是因为8之前的默认JDK版本无法存储和/或从字节码中检索参数名称。但是对于JDK 8(或使用诸如Paranamer之类的帮助程序库,或诸如Scala或Kotlin之类的其他JVM语言),指定名称是可选的。
或者,这也可以在龙目岛版本1.18.3
或更高版本中很好地工作,您可以在其中将lombok.copyableAnnotations += com.fasterxml.jackson.annotation.JsonProperty
添加到lombok.config
并因此将JsonProperty
注释复制到构造函数,因为您确实要用它注释所有字段(无论如何,imo都应该这样做)。要将@JsonCreator
注释放在构造函数上,可以使用实验性的onX feature。将lombok的@Value
用于不可变的数据类,则DTO可能看起来像这样(未经测试):
@Value
@AllArgsConstructor(onConstructor = @__(@JsonCreator))
class School {
@JsonProperty("schoolId")
String schoolId;
@JsonProperty("schoolName")
String schoolName;
}