Không thể bắt Jackson và Lombok làm việc cùng nhau


92

Tôi đang thử nghiệm kết hợp giữa Jackson và Lombok. Đó là những lớp học của tôi:

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

}

Đó là những JAR mà tôi đang thêm vào classpth:

Tôi đang biên dịch nó bằng Netbeans (Tôi không nghĩ rằng điều này thực sự có liên quan, nhưng dù sao tôi cũng đang báo cáo điều này để làm cho nó có thể tái tạo một cách hoàn hảo và trung thực). Năm JAR ở trên được giữ trong một thư mục có tên " lib" bên trong thư mục dự án (cùng với " src", " nbproject", " test" và " build"). Tôi đã thêm chúng vào Netbeans thông qua nút " Thêm JAR / Thư mục " trong thuộc tính của dự án và chúng được liệt kê theo thứ tự chính xác như danh sách ở trên. Dự án là một dự án loại "ứng dụng Java" tiêu chuẩn.

Hơn nữa, dự án Netbeans được định cấu hình để " KHÔNG biên dịch khi lưu ", " tạo thông tin gỡ lỗi ", " báo cáo các API không dùng nữa ", " theo dõi các phụ thuộc java ", " xử lý chú thích activacte " và " xử lý chú thích activacte trong trình chỉnh sửa ". Không có bộ xử lý chú thích hoặc tùy chọn xử lý chú thích nào được định cấu hình rõ ràng trong Netbeans. Ngoài ra, -Xlint:alltùy chọn dòng lệnh "" được chuyển trong dòng lệnh của trình biên dịch và trình biên dịch chạy trên một máy ảo bên ngoài.

Phiên bản javac của tôi là 1.8.0_72 và phiên bản java của tôi là 1.8.0_72-b15. Netbeans của tôi là 8.1.

Dự án của tôi biên dịch tốt. Tuy nhiên, nó ném ra một ngoại lệ trong quá trình thực thi của nó. Ngoại lệ dường như không phải là bất cứ thứ gì trông dễ dàng hoặc rõ ràng có thể sửa chữa được. Đây là đầu ra, bao gồm 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

Tôi đã thử chọc ngẫu nhiên với @Value@AllArgsConstructorchú thích, nhưng tôi không thể làm cho nó tốt hơn.

Tôi đã google ngoại lệ và tìm thấy một báo cáo lỗi cũ trên jacksonmột báo cáo khác đang mở, nhưng dường như có liên quan đến một thứ khác . Tuy nhiên, điều này vẫn không cho biết bất cứ điều gì về lỗi này là gì hoặc làm thế nào để sửa chữa nó. Ngoài ra, tôi không thể tìm thấy bất cứ điều gì hữu ích khi tìm kiếm ở một nơi khác.

Vì những gì tôi đang cố gắng làm là sử dụng rất cơ bản của cả lombok và jackson, nên có vẻ kỳ lạ là tôi không thể tìm thấy bất kỳ thông tin hữu ích nào về cách giải quyết vấn đề này. Có lẽ tôi đã bỏ lỡ điều gì đó?

Ngoài việc chỉ nói " không sử dụng lombok " hoặc " không sử dụng jackson ", có ai có ý kiến ​​gì về cách giải quyết vấn đề này không?

Câu trả lời:


58

Nếu bạn muốn bất biến nhưng một POJO có thể nối tiếp hóa json bằng cách sử dụng lombok và jackson. Sử dụng chú thích mới jacksons trên trình tạo lomboks của bạn @JsonPOJOBuilder(withPrefix = "") Tôi đã thử giải pháp này và nó hoạt động rất tốt. Sử dụng mẫu

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 {

    }
}

Nếu bạn có quá nhiều lớp @Buildervà bạn không muốn mã soạn sẵn có chú thích trống, bạn có thể ghi đè bộ đánh chặn chú thích để trốngwithPrefix

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

Và bạn có thể loại bỏ lớp người xây dựng trống bằng @JsonPOJOBuilderchú thích.


Liên kết đến một giải pháp được hoan nghênh, nhưng hãy đảm bảo rằng câu trả lời của bạn hữu ích nếu không có nó: thêm ngữ cảnh xung quanh liên kết để những người dùng đồng nghiệp của bạn sẽ biết nó là gì và tại sao nó ở đó, sau đó trích dẫn phần có liên quan nhất của trang bạn ' đang liên kết lại trong trường hợp trang đích không có sẵn. Các câu trả lời ít hơn một liên kết có thể bị xóa.
geisterfurz007

Giải pháp này giữ tính bất biến trong khi giải quyết các vấn đề về deserialisation dựa trên hàm tạo chết não của Jackson (hàm tạo đa trường yêu cầu @JsonProperty trên mỗi đối số của hàm tạo, thay vì thử một cái gì đó thông minh). (Tôi đã thử onConstructor _ = {@ JsonCreator} mà không gặp may)
JeeBee

36

Bất biến + Lombok + Jackson có thể đạt được theo cách tiếp theo:

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
Không phải AllArgsConstructorđã là một phần của @Valuechú thích?
Zon

8
Việc thêm một hàm rõ ràng @NoArgsConstructorsẽ ghi đè hàm tạo được tạo bởi @Valuechú thích, vì vậy bạn phải thêm phần bổ sung@AllArgsConstructor
Davio

1
Giải pháp tốt nhất mà tôi đã tìm thấy - theo ý kiến ​​của tôi - mà không có mẫu Builder. Cảm ơn.
Radouxca

15

Tôi đã thử một số cách trên và chúng đều có tính khí thất thường. Điều gì thực sự hiệu quả với tôi là câu trả lời mà tôi tìm thấy ở đây .

trên thư mục gốc của dự án, hãy thêm tệp lombok.config (nếu bạn chưa thực hiện)

lombok.config

và bên trong dán cái này

lombok.anyConstructor.addConstructorProperties=true

Sau đó, bạn có thể xác định pojos của mình như sau:

@Data
@AllArgsConstructor
public class MyPojo {

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

2
Nhưng điều này không phải là có thể biến đổi?
vphilipnyc

để không thay đổi, chỉ cần thay đổi dữ liệu bằng Getter và Builder hoặc RequiredArgsConstructor và đánh dấu tất cả các trường là cuối cùng
nekperu15739

8

Tôi đã gặp chính xác vấn đề tương tự, đã "giải quyết" nó bằng cách thêm suppressConstructorProperties = truethông số (sử dụng ví dụ của bạn):

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

Jackson rõ ràng không java.beans.ConstructorProperties thích chú thích được thêm vào các hàm tạo. Các suppressConstructorProperties = truetham số kể Lombok không thêm nó (nó theo mặc định).


7
suppressConstructorPropertiesbây giờ bị phản :-(
lilalinux

3
Đó không phải là vấn đề, vì mặc định mới cũng vậy false.
Michael Piefel

4

@AllArgsConstructor(suppressConstructorProperties = true)không được dùng nữa. Xác định lombok.anyConstructor.suppressConstructorProperties=true( https://projectlombok.org/features/configuration ) và Lombok chú thích thay đổi POJO từ @Valueđể @Data+ @NoArgsConstructor+ @AllArgsConstructorcông trình đối với tôi.


14
Điều này thay đổi lớp để có thể thay đổi, mà tôi cho rằng không phải là những gì OP muốn
Amit Goldstein

2

Đối với tôi, nó hoạt động khi tôi cập nhật phiên bản lombok thành: 'org.projectlombok: lombok: 1.18.0'


2

Từ câu trả lời của Jan Rieke

Kể từ lombok 1.18.4, bạn có thể định cấu hình những chú thích nào được sao chép vào các tham số của hàm tạo. Chèn cái này vào lombok.config:

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

Sau đó, chỉ cần thêm @JsonPropertyvào các trường của bạn:

...

Bạn sẽ cần @JsonProperty trên mọi trường ngay cả khi tên trùng khớp, nhưng dù sao thì đó cũng là một phương pháp hay. Bạn cũng có thể đặt các trường của mình thành công khai cuối cùng bằng cách sử dụng cái này, tôi thích cái này hơn cái getters.

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

Tuy nhiên, nó cũng sẽ hoạt động với getters (+ setters).


1
Điều này giải quyết vấn đề của tôi! Cảm ơn bạn rất nhiều! Tôi đã bị mắc kẹt trong việc tích hợp spring-boot-serialization và tuần tự hóa giữa JSON và POJO và nhận được lỗi: Can not deserialize instance of java.lang.String out of START_OBJECT token... Và điều này đã sửa nó! Tôi cần có @JsonPropertychú thích đó cho mỗi tham số hàm tạo và bổ sung đó @AllArgsConstructorcho phép lombok để thiết bị trong đó.
Đánh dấu

1

Bạn có thể khiến Jackson chơi với bất cứ thứ gì nếu bạn sử dụng mẫu "mixin" của nó . Về cơ bản, nó cung cấp cho bạn một cách để thêm các chú thích Jackson vào một lớp hiện có mà không thực sự sửa đổi lớp đó. Tôi nghiêng về việc đề xuất nó ở đây hơn là giải pháp Lombok bởi vì điều này giải quyết được vấn đề Jackson đang gặp phải với tính năng Jackson, vì vậy nó có nhiều khả năng hoạt động lâu dài.


1

Tất cả các lớp của tôi đều được chú thích như thế này:

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

Nó hoạt động với tất cả các phiên bản Lombok và Jackson trong ít nhất một vài năm.

Thí dụ:

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

Và đó là nó. Lombok và Jackson chơi với nhau như một cái duyên.


9
Đây là lớp có thể thay đổi.
ŁukaszG

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

Bổ sung cho Lớp dữ liệu, nó phải được định cấu hình đúng ObjectMapper. Trong trường hợp này, nó hoạt động tốt với cấu hình ParameterNamesModule và thiết lập khả năng hiển thị của Trường và Phương thức tạo

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

Sau đó, nó sẽ hoạt động như mong đợi.


0

Tôi đang gặp vấn đề với việc Lombok không thêm ConstructorProperieschú thích nên đã đi theo hướng khác và không cho Jackson xem chú thích đó.

Thủ phạm là JacksonAnnotationIntros Inspector.findCreatorAnnotation . Để ý:

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

Cũng lưu ý JacksonAnnotationIntros rà.setConstructorPropertiesImpliesCreator :

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

Vì vậy, hai lựa chọn, hoặc thiết lập MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIESsai hoặc tạo ra một JacksonAnnotationIntrospectorbộ setConstructorPropertiesImpliesCreatorđể falsevà thiết lập này AnnotationIntrospectorvào ObjectMapperqua ObjectMapper.setAnnotationIntrospector .

Lưu ý một vài điều, tôi đang sử dụng Jackson 2.8.10 và trong phiên bản MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIESđó không tồn tại. Tôi không chắc nó đã được thêm vào phiên bản nào của Jackson. Vì vậy, nếu nó không có ở đó, hãy sử dụng JacksonAnnotationIntrospector.setConstructorPropertiesImpliesCreatorcơ chế.


0

Bạn cũng cần phải có mô-đun này. https://github.com/FasterXML/jackson-modules-java8

sau đó bật cờ -parameters cho trình biên dịch của bạn.

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

0

Tôi cũng phải vật lộn với điều này trong giây lát. Nhưng xem qua tài liệu ở đây, tôi có thể thấy rằng tham số chú thích onConstructor được coi là thử nghiệm và không được hỗ trợ tốt trên IDE của tôi (STS 4). Theo tài liệu của Jackson, các thành viên riêng tư không được (de) đăng theo mặc định. Có nhiều cách nhanh chóng để giải quyết vấn đề này.

Thêm chú thích JsonAutoDetect và đặt nó thích hợp để phát hiện các thành viên được bảo vệ / riêng tư. Điều này thuận tiện cho DTO

@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
public class SomeClass

Thêm một hàm gốc với chú thích @JsonCreator, điều này hoạt động tốt nhất nếu bạn cần một số xác thực đối tượng hoặc các chuyển đổi bổ sung.

public class SomeClass {

   // some code here

   @JsonCreator
   public static SomeClass factory(/* params here dressing them in @JsonProperty annotations*/) {
      return new SomeClass();
   }
}

Tất nhiên, bạn cũng có thể tự thêm hàm tạo trong chính mình.


-1

Tôi đã gặp một vấn đề khác và đó là với các kiểu nguyên thủy boolean.

private boolean isAggregate;

Kết quả là nó đã gặp lỗi sau

Exception: Unrecognized field "isAggregate" (class 

Lambok cải isAggregateđể isAggregate()như một getter làm tài sản trong nội bộ để Lombok như aggregatethay isAggregate. Thư viện Jackson không thích nó và isAggregatethay vào đó nó cần tài sản.

Tôi đã cập nhật boolean nguyên thủy thành Wrapper Boolean để giải quyết vấn đề này. Có các lựa chọn khác cho bạn nếu bạn đang đối phó với booleancác loại, hãy xem phần tham khảo bên dưới.

Sol:

private Boolean isAggregate;

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


Bạn đã thử sử dụng tổng hợp thay vì isAggregate với kiểu nguyên thủy chưa? Tôi tin rằng, nó cũng có thể giải quyết được lỗi. Vui lòng xem stackoverflow.com/a/42620096/6332074
Muhammad Waqas Dilawar
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.