Làm cách nào để hủy / tuần tự hóa một đối tượng không thay đổi mà không có hàm tạo mặc định bằng ObjectMapper?


106

Tôi muốn tuần tự hóa và giải mã hóa một đối tượng không thể thay đổi bằng cách sử dụng com.fasterxml.jackson.databind.ObjectMapper.

Lớp không thay đổi trông như thế này (chỉ có 3 thuộc tính bên trong, getters và constructor):

public final class ImportResultItemImpl implements ImportResultItem {

    private final ImportResultItemType resultType;

    private final String message;

    private final String name;

    public ImportResultItemImpl(String name, ImportResultItemType resultType, String message) {
        super();
        this.resultType = resultType;
        this.message = message;
        this.name = name;
    }

    public ImportResultItemImpl(String name, ImportResultItemType resultType) {
        super();
        this.resultType = resultType;
        this.name = name;
        this.message = null;
    }

    @Override
    public ImportResultItemType getResultType() {
        return this.resultType;
    }

    @Override
    public String getMessage() {
        return this.message;
    }

    @Override
    public String getName() {
        return this.name;
    }
}

Tuy nhiên, khi tôi chạy thử nghiệm đơn vị này:

@Test
public void testObjectMapper() throws Exception {
    ImportResultItemImpl originalItem = new ImportResultItemImpl("Name1", ImportResultItemType.SUCCESS);
    String serialized = new ObjectMapper().writeValueAsString((ImportResultItemImpl) originalItem);
    System.out.println("serialized: " + serialized);

    //this line will throw exception
    ImportResultItemImpl deserialized = new ObjectMapper().readValue(serialized, ImportResultItemImpl.class);
}

Tôi nhận được ngoại lệ này:

com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class eu.ibacz.pdkd.core.service.importcommon.ImportResultItemImpl]: can not instantiate from JSON object (missing default constructor or creator, or perhaps need to add/enable type information?)
 at [Source: {"resultType":"SUCCESS","message":null,"name":"Name1"}; line: 1, column: 2]
    at 
... nothing interesting here

Ngoại lệ này yêu cầu tôi tạo một phương thức khởi tạo mặc định, nhưng đây là một đối tượng bất biến, vì vậy tôi không muốn có nó. Nó sẽ thiết lập các thuộc tính bên trong như thế nào? Nó sẽ hoàn toàn gây nhầm lẫn cho người dùng API.

Vì vậy, câu hỏi của tôi là: Bằng cách nào đó tôi có thể de / serialize các đối tượng bất biến không có hàm tạo mặc định không?


Trong khi giải mã hóa, trình giải mã không biết về bất kỳ phương thức khởi tạo nào của bạn, vì vậy nó tấn công hàm tạo mặc định. Do đó, bạn phải tạo một hàm tạo mặc định, nó sẽ không thay đổi tính bất biến. Ngoài ra tôi thấy lớp học không phải là cuối cùng, tại sao vậy? ai cũng có thể ghi đè chức năng phải không?
Abhinaba Basu

Lớp học không phải là cuối cùng, vì tôi quên viết nó ở đó, cảm ơn bạn đã chú ý :)
Michal

Câu trả lời:


149

Để cho Jackson biết cách tạo một đối tượng cho quá trình deserialization, hãy sử dụng @JsonCreator@JsonPropertychú thích cho các hàm tạo của bạn, như sau:

@JsonCreator
public ImportResultItemImpl(@JsonProperty("name") String name, 
        @JsonProperty("resultType") ImportResultItemType resultType, 
        @JsonProperty("message") String message) {
    super();
    this.resultType = resultType;
    this.message = message;
    this.name = name;
}

1
+1 cảm ơn Sergey. Nó hoạt động .. tuy nhiên tôi đã gặp sự cố ngay cả sau khi sử dụng chú thích JsonCreator .. nhưng sau một số xem xét, tôi nhận ra rằng tôi đã quên sử dụng chú thích RequestBody trong tài nguyên của mình. Bây giờ nó hoạt động .. Thanx một lần nữa.
Rahul

4
Điều gì sẽ xảy ra nếu bạn không thể thêm một hàm tạo mặc định hoặc @JsonCreator@JsonPropertychú thích bởi vì bạn không có quyền truy cập vào POJO để thay đổi nó? Có cách nào để giải mã hóa đối tượng không?
Jack Straw

1
@JackStraw 1) lớp con từ đối tượng và ghi đè lên tất cả các getters và setters. 2) viết serializer / deserializer của riêng bạn cho lớp và sử dụng nó (tìm kiếm "tùy chỉnh serializer" 3) quấn đối tượng và viết serializer / deserializer chỉ cho một thuộc tính (điều này có thể cho phép bạn thực hiện ít công việc hơn là viết một serializer cho chính lớp đó, nhưng có thể không).
ErikE

+1 bình luận của bạn đã cứu tôi! Mọi giải pháp cho đến nay mà tôi đang tìm là tạo phương thức khởi tạo mặc định ...
Nilson Aguiar

34

Bạn có thể sử dụng một phương thức khởi tạo mặc định riêng, Jackson sau đó sẽ điền vào các trường thông qua phản chiếu ngay cả khi chúng là cuối cùng riêng tư.

CHỈNH SỬA: Và sử dụng phương thức khởi tạo mặc định được bảo vệ / gói được bảo vệ cho các lớp cha nếu bạn có quyền thừa kế.


Chỉ để xây dựng trên câu trả lời này, nếu lớp bạn muốn deserialise mở rộng một lớp khác thì bạn có thể đặt hàm tạo mặc định của siêu lớp (hoặc cả hai lớp) được bảo vệ.
KWILLIAMS

3

Câu trả lời đầu tiên của Sergei Petunin là đúng. Tuy nhiên, chúng ta có thể đơn giản hóa mã bằng cách loại bỏ các chú thích @JsonProperty dư thừa trên mỗi tham số của hàm tạo.

Nó có thể được thực hiện bằng cách thêm com.fasterxml.jackson.module.paramnames.ParameterNamesModule vào ObjectMapper:

new ObjectMapper()
        .registerModule(new ParameterNamesModule(JsonCreator.Mode.PROPERTIES))

(Btw: mô-đun này được đăng ký theo mặc định trong SpringBoot. Nếu bạn sử dụng bean ObjectMapper từ JacksonObjectMapperConfiguration hoặc nếu bạn tạo ObjectMapper của riêng mình với bean Jackson2ObjectMapperBuilder thì bạn có thể bỏ qua đăng ký thủ công mô-đun)

Ví dụ:

public class FieldValidationError {
  private final String field;
  private final String error;

  @JsonCreator
  public FieldValidationError(String field,
                              String error) {
    this.field = field;
    this.error = error;
  }

  public String getField() {
    return field;
  }

  public String getError() {
    return error;
  }
}

và ObjectMapper deserializing json này mà không có bất kỳ lỗi nào:

{
  "field": "email",
  "error": "some text"
}
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.