Can't make Jackson and Lombok work together

匿名 (未验证) 提交于 2019-12-03 02:56:01

问题:

I am experimenting in combining Jackson and Lombok. Those are my classes:

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

Those are the JARs that I'm adding into the classpth:

I am compiling it with Netbeans (I don't think that this is really relevant, but I am reporting this anyway to make it perfectly and faithfully reproducible). The five JARs above are kept in a folder called "lib" inside the project folder (along with "src", "nbproject", "test" and "build"). I added them to Netbeans via the "Add JAR/Folder" button in the project properties and they are listed in the exact order as the list above. The project is a standard "Java application" type project.

Further, the Netbeans project is configured to "do NOT compile on save", "generate debugging info", "report deprecated APIs", "track java dependencies", "activacte annotation proccessing" and "activacte annotation proccessing in the editor". No annotation processor or annotation processing option is explicitly configured in Netbeans. Also, the "-Xlint:all" command line option is passed in the compiler command line, and the compiler runs on an external VM.

My javac's version is 1.8.0_72 and my java's version is 1.8.0_72-b15. My Netbeans is 8.1.

My project compiles fine. However, it throws an exception in its execution. The exception don't seems to be anything that looks easily or obvious fixable. Here is the output, including the 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 

I already tried about randomly poking with the @Value and @AllArgsConstructor annotations, but I couldn't make it any better.

I google'd the exception and found an old bug report on jackson, and another one that is open, but seems to be related to something else. However, this still do not tells anything about what is this bug or how to fix it. Also, I could not find anything useful looking that somewhere else.

Since what I am trying to do is very basic usage of both lombok and jackson, it seems odd that I couldn't find any more useful information about how to workaround this issue. Maybe I missed something?

Other than just saying "don't use lombok" or "don't use jackson", do anybody has any idea about how to solve this?

回答1:

I had exactly the same issue, "solved" it by adding the suppressConstructorProperties = true parameter (using your example):

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

Jackson apparently does not like the java.beans.ConstructorProperties annotation added to constructors. The suppressConstructorProperties = true parameter tells Lombok not to add it (it does by default).



回答2:

@AllArgsConstructor(suppressConstructorProperties = true) is deprecated. Define lombok.anyConstructor.suppressConstructorProperties=true (https://projectlombok.org/features/configuration) and change POJO's lombok annotation from @Value to @Data + @NoArgsConstructor + @AllArgsConstructor works for me.



回答3:

If you want immutable but a json serializable POJO using lombok and jackson. Use jacksons new annotation on your lomboks builder @JsonPOJOBuilder(withPrefix = "") I tried this solution and it works very well. Sample usage

    import com.fasterxml.jackson.databind.annotation.JsonDeserialize;     import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;     import lombok.*;      @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 {      } } 


回答4:

You can get Jackson to play with just about anything if you use its "mixin" pattern. Basically, it gives you a way to add Jackson annotations onto an existing class without actually modifying that class. I'm leaning towards recommending it here rather than a Lombok solution because this is solves a problem Jackson is having with a Jackson feature, so it's more likely to work long-term.



回答5:

I've all my classes annotated like this:

@JsonAutoDetect(fieldVisibility = Visibility.ANY) @JsonInclude(JsonInclude.Include.NON_DEFAULT) @Data @Accessors(fluent = true) @NoArgsConstructor @AllArgsConstructor 

It worked with all Lombok and Jackson versions for, at least, a couple of years.

Example:

@JsonAutoDetect(fieldVisibility = Visibility.ANY) @JsonInclude(JsonInclude.Include.NON_DEFAULT) @Data @Accessors(fluent = true) @NoArgsConstructor @AllArgsConstructor public class Person {     String id;     String first;     String last; } 

And that's it. Lombok and Jackson play together like a charm.



回答6:

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

Additional to the Data Class, it should be correct configured the ObjectMapper. In this case, it is working ok with a ParameterNamesModule configuration, and setting visibility of Fields and Creator Methods

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

Then it should work as expected.



回答7:

I was having issues with getting Lombok to not add the ConstructorProperies annotation so went the other way and disabled Jackson from looking at that annotation.

The culprit is JacksonAnnotationIntrospector.findCreatorAnnotation. Notice:

if (_cfgConstructorPropertiesImpliesCreator             && config.isEnabled(MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES) 

Also notice JacksonAnnotationIntrospector.setConstructorPropertiesImpliesCreator:

public JacksonAnnotationIntrospector setConstructorPropertiesImpliesCreator(boolean b) {     _cfgConstructorPropertiesImpliesCreator = b;     return this; } 

So two options, either set the MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES to false or create a JacksonAnnotationIntrospector set setConstructorPropertiesImpliesCreator to false and set this AnnotationIntrospector into the ObjectMapper via ObjectMapper.setAnnotationIntrospector.

Notice a couple things, I am using Jackson 2.8.10 and in that version MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES does not exist. I am not sure in which version of Jackson it was added. So if it is not there, use the JacksonAnnotationIntrospector.setConstructorPropertiesImpliesCreator mechanism.



回答8:

You need to have this module as well. https://github.com/FasterXML/jackson-modules-java8

then turn on -parameters flag for your compiler.

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


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!