不能让杰克逊和龙目岛一起工作


91

我正在尝试结合杰克逊和龙目岛。这些是我的课程:

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:

我正在用Netbeans进行编译(我认为这并没有真正的意义,但是无论如何,我都在报告此问题,以使其能够完美,忠实地再现)。上面的五个JAR保存在lib项目文件夹中的一个名为“ ”的文件夹中(以及“ src”,“ nbproject”,“ test”和“ build”)。我通过项目属性中的“添加JAR /文件夹”按钮将它们添加到Netbeans中,它们的排列顺序与上面的列表完全相同。该项目是标准的“ Java应用程序”类型的项目。

此外,Netbeans项目配置为“在保存时不编译”,“生成调试信息”,“报告已弃用的API ”,“跟踪Java依赖项”,“ Activacte注释处理”和“在编辑器中进行Activacte注释处理”。Netbeans中未显式配置任何注释处理器或注释处理选项。另外,-Xlint:all在编译器命令行中传递了“ ”命令行选项,并且编译器在外部VM上运行。

我的javac版本是1.8.0_72,而我的java版本是1.8.0_72-b15。我的Netbeans是8.1。

我的项目编译正常。但是,它在执行过程中引发异常。似乎没有什么异常或显而易见的可修复异常。这是输出,包括stacktrace:

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注释随意戳戳,但是我无法做得更好。

我用谷歌搜索了一个异常,发现了一个关于杰克逊的旧错误报告另一个是打开的,但似乎与其他东西有关。但是,这仍然不能说明该错误是什么或如何修复。另外,在其他地方找不到任何有用的东西。

由于我想做的是龙目岛和杰克逊的基本用法,所以我找不到关于如何解决此问题的更多有用信息,这似乎很奇怪。也许我错过了什么?

除了只说“不要使用龙目岛”或“不要使用杰克逊”外,有人对如何解决这个问题有任何想法吗?

Answers:


58

如果您想要使用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注释的空生成器类。


欢迎使用指向解决方案的链接,但是请确保没有该链接的情况下,您的回答是有用的:在该链接周围添加上下文,以便您的其他用户可以了解它的含义和含义,然后引用您所使用页面中最相关的部分如果目标页面不可用,请重新链接。只是链接的答案可能会被删除。
geisterfurz007

该解决方案在解决杰克逊基于脑死亡的构造函数反序列化问题时保持不变性(多字段构造函数在每个构造函数参数上都需要@JsonProperty,而不是尝试一些聪明的事情)。(我曾在没有运气的情况下尝试过onConstructor _ = {@ JsonCreator})
JeeBee

35

不可变+龙目岛+杰克逊可以通过以下方式实现:

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);
    }
}

4
还不AllArgsConstructor@Value注释的一部分吗?
Zon

8
添加一个显式@NoArgsConstructor重写将覆盖由@Value注释生成的构造函数,因此您必须添加额外的内容@AllArgsConstructor
Davio

1
我发现的最佳解决方案-我认为-没有Builder模式。谢谢。
Radouxca

13

我尝试了以上几种方法,但它们都很气质。对我真正起作用的是我在这里找到的答案。

在项目的根目录上,添加一个lombok.config文件(如果尚未完成)

lombok.config

并在里面粘贴

lombok.anyConstructor.addConstructorProperties=true

然后,您可以像下面这样定义您的pojos:

@Data
@AllArgsConstructor
public class MyPojo {

    @JsonProperty("Description")
    private String description;
    @JsonProperty("ErrorCode")
    private String errorCode;
}

2
但这不可变吗?
vphilipnyc

8

我遇到了完全相同的问题,通过添加suppressConstructorProperties = true参数(使用您的示例)“解决了”它:

@Value
@Wither
@AllArgsConstructor(suppressConstructorProperties = true)
public class TestFoo {
    @JsonProperty("xoom")
    private String x;
    private int z;
}

杰克逊显然不喜欢java.beans.ConstructorProperties 添加到构造函数的注释。该suppressConstructorProperties = true参数告诉Lombok不要添加它(默认情况下会添加)。


7
suppressConstructorProperties现在已弃用:-(
lilalinux

3
这不是问题,因为新的默认值也是false
Michael Piefel '18

4

@AllArgsConstructor(suppressConstructorProperties = true)不推荐使用。定义lombok.anyConstructor.suppressConstructorProperties=truehttps://projectlombok.org/features/configuration)并将POJO的lombok注释从更改@Value@Data+ @NoArgsConstructor+@AllArgsConstructor对我有用。


14
这改变了类是可变的,我以为是不是有什么OP想
阿米特·戈尔茨坦

2

对我来说,当我将lombok版本更新为:'org.projectlombok:lombok:1.18.0'时,它起作用了


2

简·里克的回答

从lombok 1.18.4开始,您可以配置将哪些注释复制到构造函数参数。将此插入您的lombok.config

lombok.copyableAnnotations += com.fasterxml.jackson.annotation.JsonProperty

然后只需添加@JsonProperty到您的字段:

...

即使名称匹配,您也需要在每个字段上使用@JsonProperty,但这仍然是遵循的好习惯。您也可以使用此方法将字段设置为public final,与getter相比,我更喜欢这样做。

@ToString
@EqualsAndHashCode
@Wither
@AllArgsConstructor(onConstructor=@__(@JsonCreator))
public class TestFoo {
    @JsonProperty("xoom")
    public final String x;
    @JsonProperty("z")
    public final int z;
}

它也应该与吸气剂(+ setters)一起工作。


1
这解决了我的问题!非常感谢!我一直停留在JSON和POJO,并得到错误之间的弹簧引导整合和序列化:Can not deserialize instance of java.lang.String out of START_OBJECT token...这定了!我需要有@JsonProperty标注为每个构造函数的参数,并且除了@AllArgsConstructor允许龙目文书英寸
马克

1

如果您使用Jackson的“ mixin”模式,那么您几乎可以玩任何东西。基本上,它为您提供了一种将Jackson注释添加到现有类上而不实际修改该类的方法。我倾向于在这里推荐它,而不是Lombok解决方案,因为这是为了解决Jackson的Jackson功能所存在的问题,因此它更可能长期运行。


1

我所有的班级都这样注释:

@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;
}

就是这样。龙目岛(Lombok)和杰克逊(Jackson)像魅力一样一起玩。


9
这是可变的类。
ŁukaszG

0
@JsonInclude(JsonInclude.Include.NON_NULL)
@Data
public class Person {
   String id;
   String first;
   String last;
}

除数据类外,还应正确配置ObjectMapper。在这种情况下,可以使用ParameterNamesModule配置并设置Fields和Creator方法的可见性

    om.registerModule(new ParameterNamesModule());
    om.setVisibility(FIELD, JsonAutoDetect.Visibility.ANY);
    om.setVisibility(CREATOR, JsonAutoDetect.Visibility.ANY);

然后它应该按预期工作。


0

我在让Lombok不添加ConstructorProperies注释方面遇到问题,因此反过来又使Jackson无法查看该注释。

罪魁祸首是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 ,要么将其创建为,然后通过ObjectMapper.setAnnotationIntrospectorJacksonAnnotationIntrospectorsetConstructorPropertiesImpliesCreatorfalse设置AnnotationIntrospector为。ObjectMapper

请注意,我使用的是Jackson 2.8.10,但该版本MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES不存在。我不确定它是在哪个版本的Jackson中添加的。因此,如果不存在,请使用该JacksonAnnotationIntrospector.setConstructorPropertiesImpliesCreator机制。



0

我也为此挣扎了一下。但是,通过查看这里的文档 我可以看到onConstructor批注参数被认为是实验性的,并且在我的IDE(STS 4)中不受很好的支持。根据Jackson的文档,默认情况下,不对私人成员进行反序列化。有快速的方法可以解决此问题。

添加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();
   }
}

当然,您也可以手动在自己中添加构造函数。


-1

我有一个不同的问题,那就是布尔基本类型。

private boolean isAggregate;

结果抛出了以下错误

Exception: Unrecognized field "isAggregate" (class 

Lambok转换isAggregateisAggregate()作为一个getter使得内部财产龙目岛作为aggregate代替isAggregate。Jackson库不喜欢它,而是需要isAggregate属性。

我将原始布尔值更新为Wrapper布尔值,以解决此问题。如果要处理boolean类型,还有其他选择,请参见下面的参考。

索尔:

private Boolean isAggregate;

参考:https : //www.baeldung.com/lombok-getter-boolean


您是否尝试使用具有原始类型的聚合而不是isAggregate?我相信,它也可以解决错误。另请参见此stackoverflow.com/a/42620096/6332074
Muhammad Waqas Dilawar
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.