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

时间:2016-09-08 01:58:57

标签: java netbeans 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:

我正在使用Netbeans进行编译(我不认为这是真的相关,但无论如何我都在报告这一点,以使其完美且忠实地再现)。上面的五个JAR保存在项目文件夹中名为“lib”的文件夹中(以及“src”,“nbproject”,“test”和“{ {1}}“)。我通过项目属性中的“添加JAR /文件夹”按钮将它们添加到Netbeans中,它们按照上面列表的确切顺序列出。该项目是标准的“Java应用程序”类型项目。

此外,Netbeans项目配置为“不在编译时保存”,“生成调试信息”,“报告已弃用的API “,”跟踪java依赖项“,”激活注释处理“和”激活注释在编辑器中处理“。 Netbeans中未明确配置注释处理器或注释处理选项。此外,“build”命令行选项在编译器命令行中传递,编译器在外部VM上运行。

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

我的项目编译得很好。但是,它会在执行时抛出异常。例外似乎不是任何容易或明显可修复的东西。这是输出,包括stacktrace:

-Xlint:all

我已经尝试过使用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注释随机戳,但我无法做得更好。

我查看了例外found an old bug report on jacksonanother one that is open, but seems to be related to something else。但是,这仍然没有说明这个错误是什么或如何解决它。此外,我找不到任何其他有用的东西。

由于我要做的是lombok和jackson的非常基本的用法,我似乎找不到有关如何解决此问题的更多有用信息似乎很奇怪。也许我错过了什么?

除了说“不使用lombok ”或“不使用jackson ”之外,有人知道如何解决这个问题吗?

20 个答案:

答案 0 :(得分:26)

如果你想使用lombok和jackson的不可变但是json可序列化的POJO。 在lomboks构建器@JsonPOJOBuilder(withPrefix = "")上使用jacksons new注释 我试过这个解决方案,效果很好。 样本用法

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注释删除空的构建器类。

答案 1 :(得分:5)

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

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

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

答案 2 :(得分:4)

如果您想将 @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 {
    }
}
  • 你需要做一些手工工作
  • 仍然比自己编写构建器要好得多
  • 另请注意,诸如 @JsonIgnorePropertie 之类的其他属性会用于构建器

另一个缺点是重构不会自动重命名 MyDtoBuilder。我希望在未来的 Lombok/Jackson 版本中可以解决这个问题。

更新:我找到了另一个解决方案(用 lombok 1.18.20 和 spring boot 2.4.5 测试),作为选项 1 添加。

答案 3 :(得分:4)

lombok.anyConstructor.suppressConstructorProperties=true已弃用。定义@Valuehttps://projectlombok.org/features/configuration)并将POJO的lombok注释从@Data更改为@NoArgsConstructor + @AllArgsConstructor + # Compute the XOR of all numbers from 0 to |A| - 1 and all entries in A # Assume A is a list of integers miss_XOR_dup = functools.reduce(lambda v, i: v ^ i[0] ^ i[1], enumerate(A), 0) 适用于我。

答案 4 :(得分:2)

我尝试了以上几种方法,而且都很气质。 对我真正有用的是我找到的here答案。

在项目的根目录中添加一个文件(如果尚未完成的话)

  

lombok.config

并在其中粘贴

lombok.anyConstructor.addConstructorProperties=true

然后,您可以按以下方式定义pojos:

@Data
@AllArgsConstructor
public class MyPojo {

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

答案 5 :(得分:2)

我建议您使用 Gson,因为它不会给您带来所有这些麻烦。

我在 Spring Boot 应用程序中添加了这个

# Build a small Pixel CNN++ model to train on MNIST. import tensorflow as tf import tensorflow_datasets as tfds import tensorflow_probability as tfp tfd = tfp.distributions tfk = tf.keras tfkl = tf.keras.layers tf.enable_v2_behavior() # Load MNIST from tensorflow_datasets data = tfds.load('mnist') train_data, test_data = data['train'], data['test'] def image_preprocess(x): x['image'] = tf.cast(x['image'], tf.float32) return (x['image'],) # (input, output) of the model batch_size = 16 train_it = train_data.map(image_preprocess).batch(batch_size).shuffle(1000) image_shape = (28, 28, 1) # Define a Pixel CNN network dist = tfd.PixelCNN( image_shape=image_shape, num_resnet=1, num_hierarchies=2, num_filters=32, num_logistic_mix=5, dropout_p=.3, ) # Define the model input image_input = tfkl.Input(shape=image_shape) # Define the log likelihood for the loss fn log_prob = dist.log_prob(image_input) # Define the model model = tfk.Model(inputs=image_input, outputs=log_prob) model.add_loss(-tf.reduce_mean(log_prob)) # Compile and train the model model.compile( optimizer=tfk.optimizers.Adam(.001), metrics=[]) model.fit(train_it, epochs=10, verbose=True) # sample five images from the trained model samples = dist.sample(5)

连同maven中的依赖,我解决了所有问题。我不需要修改我的 lombok 注释 pojo

答案 6 :(得分:2)

对我来说,当我将lombok版本更新为: 'org.projectlombok:lombok:1.18.0'

答案 7 :(得分:1)

你也需要这个模块。 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>

答案 8 :(得分:1)

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

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

答案 9 :(得分:1)

如果使用"mixin" pattern,你可以让杰克逊玩几乎任何东西。基本上,它为您提供了一种方法,可以将Jackson注释添加到现有类中,而无需实际修改该类。我倾向于在这里推荐它而不是Lombok解决方案,因为这解决了杰克逊在Jackson功能方面遇到的问题,因此它更有可能长期工作。

答案 10 :(得分:1)

From Jan Rieke's Answer

  

从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")
    private final String x;
    @JsonProperty("z")
    private final int z;
}

答案 11 :(得分:1)

它可以做得更简单,不需要额外的注释,问题可能出在继承上,即子类也应该是可反序列化的。所以,我的例子:

要求:

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

它:

  1. 允许构建包含父级字段的对象
  2. 使用默认的 ObjectMapper 和 Jackson 进行序列化/反序列化
  3. 父类和子类的实例是不可变的

我对其他例子的建议:

  1. 尽量不要在特定类上放置自定义注释,这会使其不均匀。不管怎样,总有一天你会找到一个通用的解决方案。
  2. 尽量不要在构造函数的任何字段上放置 Jackson 注释,当 Jackson 能够在没有任何注释的情况下进行序列化/反序列化时,它会产生耦合。
  3. 请勿将 @AllArgsConstructor 用于不可变实体。当您的类只有 final 字段时,概念上正确的是 @RequiredArgsConstructor,这就是您保证类客户端始终仅依赖具有不可变实体的构造函数的方式。 @AllArgsConstructor 是否会导致传递空值。

答案 12 :(得分:0)

我遇到了让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,并将此AnnotationIntrospector设置为ObjectMapper通过ObjectMapper.setAnnotationIntrospector

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

答案 13 :(得分:0)

我也为此挣扎了一下。但是请仔细阅读文档here 我可以看到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();
   }
}

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

答案 14 :(得分:0)

尝试添加@NoArgsConstructor

答案 15 :(得分:0)

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

private boolean isAggregate;

其结果是引发以下错误

Exception: Unrecognized field "isAggregate" (class 

Lambok作为吸气剂将isAggregate转换为isAggregate(),使该属性在内部以aggregate而不是isAggregate的身份进入lombok。 Jackson库不喜欢它,它需要isAggregate属性。

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

Sol:

private Boolean isAggregate;

ref:https://www.baeldung.com/lombok-getter-boolean

答案 16 :(得分:0)

For i As Integer = 0 To itemlist.Count
  Dim item = itemlist.Item(i);
  If item.Innerhtml = "Controller" Then
    MsgBox(item.innertext) 
    Dim iNext = i++;
    if iNext <= itemList.Count Then 
       Dim closestItem = itemList.Item(iNext)
       'Do things
    End If
    Exit For
  End If
Next

除了Data Class之外,它应该正确配置ObjectMapper。 在这种情况下,它可以正常使用ParameterNamesModule配置,并设置Fields和Creator Methods的可见性

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

然后它应该按预期工作。

答案 17 :(得分:0)

对我有用的选项

  • 这对我有用,只需在我的 bean 中添加 @AllArgsConstructor。
  • 添加mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);对象映射器实例。

答案 18 :(得分:0)

我的所有课程都注释如下:

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

就是这样。 龙目岛和杰克逊一起玩耍就像一个魅力。

答案 19 :(得分:0)

我设法保持我的类不可变,并通过使用这个 lombok 注释反序列化它们:

{{1}}