不能让杰克逊和龙目岛一起工作
Posted
技术标签:
【中文标题】不能让杰克逊和龙目岛一起工作【英文标题】:Can't make Jackson and Lombok work together 【发布时间】:2017-01-15 19:52:05 【问题描述】:我正在尝试将 Jackson 和 Lombok 结合起来。这些是我的课程:
package testelombok;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Value;
import lombok.experimental.Wither;
@Value
@Wither
@AllArgsConstructor(onConstructor=@__(@JsonCreator))
public class TestFoo
@JsonProperty("xoom")
private String x;
private int z;
package testelombok;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xebia.jacksonlombok.JacksonLombokAnnotationIntrospector;
import java.io.IOException;
public class TestLombok
public static void main(String[] args) throws IOException
TestFoo tf = new TestFoo("a", 5);
System.out.println(tf.withX("b"));
ObjectMapper om = new ObjectMapper().setAnnotationIntrospector(new JacksonLombokAnnotationIntrospector());
System.out.println(om.writeValueAsString(tf));
TestFoo tf2 = om.readValue(om.writeValueAsString(tf), TestFoo.class);
System.out.println(tf2);
这些是我添加到 classpth 中的 JAR:
龙目岛:https://projectlombok.org/downloads/lombok.jar(版本 1.16.10)
Jackson 注释:http://repo1.maven.org/maven2/com/fasterxml/jackson/core/jackson-annotations/2.8.2/jackson-annotations-2.8.2.jar
杰克逊核心:http://repo1.maven.org/maven2/com/fasterxml/jackson/core/jackson-core/2.8.2/jackson-core-2.8.2.jar
杰克逊数据绑定:http://repo1.maven.org/maven2/com/fasterxml/jackson/core/jackson-databind/2.8.2/jackson-databind-2.8.2.jar
杰克逊-龙目岛:http://repo1.maven.org/maven2/io/paradoxical/jackson-lombok/1.1/jackson-lombok-1.1.jar
我正在使用 Netbeans 编译它(我认为这并不真正相关,但无论如何我都会报告它以使其完美且忠实地重现)。上面的五个 JAR 保存在项目文件夹内名为“lib
”的文件夹中(以及“src
”、“nbproject
”、“test
”和“build
”)。我通过项目属性中的“Add JAR/Folder”按钮将它们添加到 Netbeans,它们按照上面列表的确切顺序列出。该项目是一个标准的“Java 应用程序”类型的项目。
此外,Netbeans 项目配置为“保存时不编译”、“生成调试信息”、“报告已弃用的 API ", "track java dependencies", "activacte annotation proccessing" 和 "activacte annotation proccessing in the editor"。 Netbeans 中没有显式配置注释处理器或注释处理选项。另外,“-Xlint:all
”命令行选项在编译器命令行中传递,编译器运行在外部VM上。
我的 javac 版本是 1.8.0_72,我的 java 版本是 1.8.0_72-b15。我的 Netbeans 是 8.1。
我的项目编译良好。但是,它在执行过程中会引发异常。例外似乎不是任何看起来容易或明显可修复的东西。这是输出,包括堆栈跟踪:
TestFoo(x=b, z=5)
"z":5,"xoom":"a"
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Argument #0 of constructor [constructor for testelombok.TestFoo, annotations: interface java.beans.ConstructorProperties=@java.beans.ConstructorProperties(value=[x, z]), interface com.fasterxml.jackson.annotation.JsonCreator=@com.fasterxml.jackson.annotation.JsonCreator(mode=DEFAULT)] has no property name annotation; must have name when multiple-parameter constructor annotated as Creator
at [Source: "z":5,"xoom":"a"; line: 1, column: 1]
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:296)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:269)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:244)
at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:142)
at com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:475)
at com.fasterxml.jackson.databind.ObjectMapper._findRootDeserializer(ObjectMapper.java:3890)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3785)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2833)
at testelombok.TestLombok.main(TestLombok.java:14)
Caused by: java.lang.IllegalArgumentException: Argument #0 of constructor [constructor for testelombok.TestFoo, annotations: interface java.beans.ConstructorProperties=@java.beans.ConstructorProperties(value=[x, z]), interface com.fasterxml.jackson.annotation.JsonCreator=@com.fasterxml.jackson.annotation.JsonCreator(mode=DEFAULT)] has no property name annotation; must have name when multiple-parameter constructor annotated as Creator
at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory._addDeserializerConstructors(BasicDeserializerFactory.java:511)
at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory._constructDefaultValueInstantiator(BasicDeserializerFactory.java:323)
at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory.findValueInstantiator(BasicDeserializerFactory.java:253)
at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.buildBeanDeserializer(BeanDeserializerFactory.java:219)
at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.createBeanDeserializer(BeanDeserializerFactory.java:141)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer2(DeserializerCache.java:406)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer(DeserializerCache.java:352)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:264)
... 7 more
我已经尝试过使用 @Value
和 @AllArgsConstructor
注释随机戳,但我无法让它变得更好。
我用谷歌搜索了异常和found an old bug report on jackson 和another one that is open, but seems to be related to something else。但是,这仍然没有说明这个错误是什么或如何修复它。另外,我在其他地方找不到任何有用的东西。
由于我正在尝试做的是 lombok 和 jackson 的非常基本的用法,因此我找不到任何有关如何解决此问题的有用信息似乎很奇怪。也许我错过了什么?
除了说“不要使用 lombok”或“不要使用 jackson”之外,有人知道如何解决这个问题吗? p>
【问题讨论】:
【参考方案1】:如果您想要不可变但使用 lombok 和 jackson 的 json 可序列化 POJO。
在您的 lomboks 构建器上使用 jacksons 新注释 @JsonPOJOBuilder(withPrefix = "")
我尝试了这个解决方案,效果很好。
示例使用
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import lombok.Builder;
import lombok.Value;
@JsonDeserialize(builder = Detail.DetailBuilder.class)
@Value
@Builder
public class Detail
private String url;
private String userName;
private String password;
private String scope;
@JsonPOJOBuilder(withPrefix = "")
public static class DetailBuilder
如果您有太多带有@Builder
的类并且您不希望样板代码为空注释,您可以覆盖注释拦截器以使withPrefix
为空
mapper.setAnnotationIntrospector(new JacksonAnnotationIntrospector()
@Override
public JsonPOJOBuilder.Value findPOJOBuilderConfig(AnnotatedClass ac)
if (ac.hasAnnotation(JsonPOJOBuilder.class)) //If no annotation present use default as empty prefix
return super.findPOJOBuilderConfig(ac);
return new JsonPOJOBuilder.Value("build", "");
);
您可以删除带有@JsonPOJOBuilder
注释的空构建器类。
【讨论】:
欢迎提供解决方案的链接,但请确保您的答案在没有它的情况下有用:add context around the link 这样您的其他用户就会知道它是什么以及为什么会出现,然后引用最相关的您链接到的页面的一部分,以防目标页面不可用。 Answers that are little more than a link may be deleted. 此解决方案在解决 Jackson 的基于脑死亡构造函数的反序列化问题的同时保持不变性(多字段构造函数在每个构造函数参数上都需要 @JsonProperty,而不是尝试一些智能的东西)。 (我试过 onConstructor_=@JsonCreator 没有运气)【参考方案2】:Immutable + Lombok + Jackson 可以通过以下方式实现:
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.Value;
@Value
@NoArgsConstructor(force = true, access = AccessLevel.PRIVATE)
@AllArgsConstructor
public class LocationDto
double longitude;
double latitude;
class ImmutableWithLombok
public static void main(String[] args) throws Exception
ObjectMapper objectMapper = new ObjectMapper();
String stringJsonRepresentation = objectMapper.writeValueAsString(new LocationDto(22.11, 33.33));
System.out.println(stringJsonRepresentation);
LocationDto locationDto = objectMapper.readValue(stringJsonRepresentation, LocationDto.class);
System.out.println(locationDto);
【讨论】:
AllArgsConstructor
不是已经是@Value
注释的一部分了吗?
显式添加@NoArgsConstructor
会覆盖@Value
注释生成的构造函数,因此您必须添加额外的@AllArgsConstructor
我发现的最佳解决方案 - 在我看来 - 没有 Builder 模式。谢谢。
是否为我们在构造函数中添加@AllArgsConstructor
“是否”@JsonCreator
?如果是,为什么我们需要@NoArgsConstructor(force = true, access = AccessLevel.PRIVATE)
?
这是我一直在寻找的东西!回复最后一条评论,你需要他们两个,这就是为什么需要@NoArgsConstructor
,但是如果你不初始化字段(force=true)你会在设置@NoArgsConstructor
注释时出错,因为字段是final
【参考方案3】:
我尝试了上述几种方法,它们都很喜怒无常。 真正对我有用的是我找到的答案here。
在您项目的根目录中添加一个lombok.config 文件(如果您还没有这样做的话)
lombok.config
然后在里面粘贴这个
lombok.anyConstructor.addConstructorProperties=true
然后你可以像下面这样定义你的 pojo:
@Data
@AllArgsConstructor
public class MyPojo
@JsonProperty("Description")
private String description;
@JsonProperty("ErrorCode")
private String errorCode;
【讨论】:
但这不是可变的吗? 不可变,只需通过 Getter 和 Builder 或 RequiredArgsConstructor 更改数据并将所有字段标记为 final 关于为什么这在我的情况下不起作用的任何想法?我有一个 spring boot 应用程序,我已将 lombok.config 放在我的 src 中,但您的解决方案仍然无法正常工作。 这个配置并没有解决我的问题,至少在我的单元测试中没有。【参考方案4】:我遇到了完全相同的问题,通过添加 suppressConstructorProperties = true
参数“解决”了它(使用您的示例):
@Value
@Wither
@AllArgsConstructor(suppressConstructorProperties = true)
public class TestFoo
@JsonProperty("xoom")
private String x;
private int z;
Jackson 显然不喜欢在构造函数中添加 java.beans.ConstructorProperties
注释。 suppressConstructorProperties = true
参数告诉Lombok 不要添加它(默认情况下会添加)。
【讨论】:
suppressConstructorProperties
现在已弃用 :-(
这不是问题,因为新的默认值也是false
。【参考方案5】:
@AllArgsConstructor(suppressConstructorProperties = true)
已弃用。定义 lombok.anyConstructor.suppressConstructorProperties=true
(https://projectlombok.org/features/configuration) 并将 POJO 的 lombok 注释从 @Value
更改为 @Data
+ @NoArgsConstructor
+ @AllArgsConstructor
对我有用。
【讨论】:
这会将类更改为可变的,我认为这不是 OP 想要的【参考方案6】:From Jan Rieke's Answer
从lombok 1.18.4开始,可以配置复制到哪些注解 构造函数参数。将此插入您的
lombok.config
:lombok.copyableAnnotations += com.fasterxml.jackson.annotation.JsonProperty
然后将
@JsonProperty
添加到您的字段中:...
即使名称匹配,您也需要在每个字段上使用 @JsonProperty,但无论如何这是一个很好的做法。您还可以使用它将您的字段设置为 public final,我更喜欢它而不是 getters。
@ToString
@EqualsAndHashCode
@Wither
@AllArgsConstructor(onConstructor=@__(@JsonCreator))
public class TestFoo
@JsonProperty("xoom")
public final String x;
@JsonProperty("z")
public final int z;
不过,它也应该适用于 getter (+setter)。
【讨论】:
这解决了我的问题!太感谢了!我一直被困在 JSON 和 POJO 之间的 spring-boot-integration 和序列化上,并得到错误:Can not deserialize instance of java.lang.String out of START_OBJECT token
... 这解决了它!我需要为每个构造函数参数添加 @JsonProperty
注释,并且添加到 @AllArgsConstructor
允许 lombok 对其进行检测。【参考方案7】:
如果您想将@Builder
与 Jackson 一起使用,我找到了两个解决此问题的方法。
选项 1
添加私有默认 noArgs 和 allArgs 构造函数。@Builder
@Getter
@Setter
@JsonIgnoreProperties(ignoreUnknown = true)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class Person
@JsonProperty("user_name")
private String name;
选项 2
感谢this 文章。
Jackson 期望构建器方法以 .withProperty(...)
开头,但 Lombok 生成 .property(...)
。
您可以自己创建构建器类,以便向其添加 Jackson 注释。然后,Lombok 将重用这个类并将所有构建器方法添加到它。
@JsonDeserialize(builder = MyDto.MyDtoBuilder.class)
@Builder
@Getter
public class MyDto
@JsonProperty("user_id")
private String userId;
@JsonPOJOBuilder(withPrefix = "")
@JsonIgnoreProperties(ignoreUnknown = true)
public static class MyDtoBuilder
您需要做一些手工工作
仍然比自己编写 Builder 好得多
另请注意,@JsonIgnorePropertie
等其他属性会在构建器上运行
另一个缺点是重构不会自动重命名MyDtoBuilder
。我希望在未来的 Lombok/Jackson 版本中解决这个问题。
更新:我找到了另一种解决方案(使用 lombok 1.18.20 和 spring boot 2.4.5 测试),作为选项 1 添加。
【讨论】:
【参考方案8】:对我来说,当我将 lombok 版本更新为: 'org.projectlombok:lombok:1.18.0'
【讨论】:
【参考方案9】:它可以做得更简单,不需要额外的注释,问题可能出在继承上,即子类也应该是可反序列化的。所以,我的例子:
要求:
lombok.config
在项目根目录中,正文包含:
lombok.anyConstructor.addConstructorProperties=true
/** The parent class **/
@Value
@NonFinal
@SuperBuilder
@RequiredArgsConstructor
public class Animal
String name;
/** The child class **/
@Value
@SuperBuilder
@RequiredArgsConstructor
public class Cat
Long tailLength;
@ConstructorProperties("tailLength", "name)
public Cat(Long tailLength, String name)
super(name);
this.tailLength = tailLength;
它:
-
允许构建包含父字段的对象
使用默认的
ObjectMapper
和 Jackson 进行序列化/反序列化
父类和子类的实例是不可变的
我对其他示例的建议:
-
尽量不要将自定义注释放在特定类上,这会使它不均匀。不管怎样,总有一天你会找到一个通用的解决方案。
尽量不要将
Jackson
注释放在构造函数的任何字段上,这会产生耦合,因为 Jackson 能够在没有任何注释的情况下进行序列化/反序列化。
不要将@AllArgsConstructor
用于不可变实体。当您的类只有 final 字段时,概念上正确的是@RequiredArgsConstructor
,这就是您保证类客户端始终仅依赖于具有不可变实体的构造函数的方式。请问@AllArgsConstructor
可能会导致传递空值。
【讨论】:
我找到的最好的解决方案,至少看起来更清晰【参考方案10】:如果你使用它的"mixin" pattern,你可以让杰克逊玩任何东西。基本上,它为您提供了一种将 Jackson 注释添加到现有类的方法,而无需实际修改该类。我倾向于在这里推荐它而不是 Lombok 解决方案,因为这解决了 Jackson 在 Jackson 功能方面遇到的问题,因此它更有可能长期工作。
【讨论】:
【参考方案11】:我的所有课程都这样注释:
@JsonAutoDetect(fieldVisibility = Visibility.ANY)
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
@Data
@Accessors(fluent = true)
@NoArgsConstructor
@AllArgsConstructor
它与所有 Lombok 和 Jackson 版本一起工作了至少几年。
例子:
@JsonAutoDetect(fieldVisibility = Visibility.ANY)
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
@Data
@Accessors(fluent = true)
@NoArgsConstructor
@AllArgsConstructor
public class Person
String id;
String first;
String last;
就是这样。 龙目岛和杰克逊一起玩就像一个魅力。
【讨论】:
这是可变类。【参考方案12】:我建议您使用 Gson,因为它不会给您带来所有这些麻烦。
我在我的 Spring Boot 应用中添加了这个
spring.mvc.converters.preferred-json-mapper=gson
连同 maven 中的依赖关系,我解决了所有问题。我不需要修改我的 lombok 注释 pojos
【讨论】:
Lombok 团队创建了 @Jacksonized 注释,它生成了 Jackson 所需的所有代码,它以更简洁的方式解决了我的所有问题,节省了大量时间。终于!【参考方案13】:这是一个使用示例
@Jacksonized
注解:
import lombok.Builder;
import lombok.extern.jackson.Jacksonized;
@Jacksonized
@Builder
public class User
private final String name;
private final String surname;
它确实需要您使用@Builder
注释。
【讨论】:
【参考方案14】:@JsonInclude(JsonInclude.Include.NON_NULL)
@Data
public class Person
String id;
String first;
String last;
除了Data Class,应该正确配置ObjectMapper。 在这种情况下,可以使用 ParameterNamesModule 配置,并设置字段和创建者方法的可见性
om.registerModule(new ParameterNamesModule());
om.setVisibility(FIELD, JsonAutoDetect.Visibility.ANY);
om.setVisibility(CREATOR, JsonAutoDetect.Visibility.ANY);
然后它应该按预期工作。
【讨论】:
【参考方案15】:我在让 Lombok 不添加 ConstructorProperies
注释时遇到问题,所以我选择了另一种方式并禁止杰克逊查看该注释。
罪魁祸首是JacksonAnnotationIntrospector.findCreatorAnnotation。注意:
if (_cfgConstructorPropertiesImpliesCreator
&& config.isEnabled(MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES)
另请注意JacksonAnnotationIntrospector.setConstructorPropertiesImpliesCreator:
public JacksonAnnotationIntrospector setConstructorPropertiesImpliesCreator(boolean b)
_cfgConstructorPropertiesImpliesCreator = b;
return this;
所以有两个选项,要么将MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES
设置为false,要么创建一个JacksonAnnotationIntrospector
,将setConstructorPropertiesImpliesCreator
设置为false
,然后通过ObjectMapper.setAnnotationIntrospector 将此AnnotationIntrospector
设置为ObjectMapper
。
请注意几件事,我使用的是 Jackson 2.8.10,而在该版本中 MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES
不存在。我不确定它是在哪个版本的 Jackson 中添加的。因此,如果它不存在,请使用JacksonAnnotationIntrospector.setConstructorPropertiesImpliesCreator
机制。
【讨论】:
【参考方案16】:你也需要有这个模块。 https://github.com/FasterXML/jackson-modules-java8
然后为您的编译器打开 -parameters 标志。
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>
【讨论】:
【参考方案17】:我也为此苦苦挣扎了一会儿。但是查看文档here 我可以看到 onConstructor 注释参数被认为是实验性的,并且在我的 IDE(STS 4)上没有得到很好的支持。根据杰克逊文档,默认情况下私有成员不会(反)序列化。有一些快速的方法可以解决这个问题。
添加 JsonAutoDetect 注释并适当设置它以检测受保护/私有成员。这对 DTO 来说很方便
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
public class SomeClass
添加带有 @JsonCreator 注释的工厂函数,如果您需要一些对象验证或额外的转换,这将最有效。
public class SomeClass
// some code here
@JsonCreator
public static SomeClass factory(/* params here dressing them in @JsonProperty annotations*/)
return new SomeClass();
当然你也可以自己手动添加构造函数。
【讨论】:
【参考方案18】:对我有用的选项
只需在我的 bean 中添加 @AllArgsConstructor 即可。 添加 mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);对象映射器实例。【讨论】:
【参考方案19】:我已经设法保持我的类不可变,并通过使用这个 lombok 注释来反序列化它们:
@NoArgsConstructor(force = true)
【讨论】:
【参考方案20】:我有一个不同的问题,它与布尔原始类型有关。
private boolean isAggregate;
结果抛出以下错误
Exception: Unrecognized field "isAggregate" (class
Lambok 将 isAggregate
转换为 isAggregate()
作为吸气剂,在内部将属性设置为 aggregate
而不是 isAggregate
。 Jackson 库不喜欢它,它需要 isAggregate
属性。
我将原始布尔值更新为 Wrapper Boolean 以解决此问题。如果您正在处理 boolean
类型,还有其他选项可供您选择,请参阅下面的参考。
索尔:
private Boolean isAggregate;
参考:https://www.baeldung.com/lombok-getter-boolean
【讨论】:
您是否尝试过使用聚合而不是原始类型的 isAggregate?我相信,它也可以解决错误。另请参阅***.com/a/42620096/6332074以上是关于不能让杰克逊和龙目岛一起工作的主要内容,如果未能解决你的问题,请参考以下文章
不能让 QTcpSocket/QTcpServer 一起工作