Jackson + Mô hình Builder?


89

Tôi muốn Jackson deserialize một lớp với hàm tạo sau:

public Clinic(String name, Address address)

Bỏ số liệu đối số đầu tiên là dễ dàng. Vấn đề là Địa chỉ được định nghĩa là:

public class Address {
  private Address(Map<LocationType, String> components)
  ...

  public static class Builder {
    public Builder setCity(String value);
    public Builder setCountry(String value);
    public Address create();
  }
}

và được xây dựng như thế này: new Address.Builder().setCity("foo").setCountry("bar").create();

Có cách nào để lấy các cặp khóa-giá trị từ Jackson để tự tạo Địa chỉ không? Ngoài ra, có cách nào để Jackson sử dụng chính lớp Builder không?

Câu trả lời:


139

Miễn là bạn đang sử dụng Jackson 2+, thì hiện tại đã có hỗ trợ cho việc này .

Trước tiên, bạn cần thêm chú thích này vào Addresslớp của mình :

@JsonDeserialize(builder = Address.Builder.class)

Sau đó, bạn cần thêm chú thích này vào Builderlớp của mình :

@JsonPOJOBuilder(buildMethodName = "create", withPrefix = "set")

Bạn có thể bỏ qua chú thích thứ hai này nếu bạn hài lòng với việc đổi tên phương thức tạo của Người xây dựng để xây dựng và các bộ đặt của Người xây dựng của bạn được đặt tiền tố bằng, thay vì đặt.

Ví dụ đầy đủ:

@JsonDeserialize(builder = Address.Builder.class)
public class Address
{
  private Address(Map<LocationType, String> components)
  ...

  @JsonPOJOBuilder(buildMethodName = "create", withPrefix = "set")
  public static class Builder
  {
    public Builder setCity(String value);
    public Builder setCountry(String value);
    public Address create();
  }
}

14
Nếu bạn muốn loại bỏ @JsonPOJOBuildertất cả các chú thích cùng nhau, hãy đổi tên "create" thành "build" và chú thích cho từng bộ thiết lập trình tạo bằng @JsonProperty.
Sam Berry

đây là vàng. Cảm ơn.
Mukul Goel

Điều này hiện đã lỗi thời, với Lombok 1.18.4, bạn có thể sử dụng @Jacksonizednó để thay thế trình tạo bên trong và chú thích jackson bằng một thứ duy nhất
Randakar

@Randakar Tôi không nghĩ tính năng này đã lỗi thời vì a) @Jackonized là một tính năng thử nghiệm mới được phát hành ở Lombok. Tôi không nghĩ rằng đó là một ý kiến ​​hay khi khuyến khích việc áp dụng các tính năng thử nghiệm một cách không cần thiết. b) câu hỏi không đề cập hoặc sử dụng Lombok. Tôi không nghĩ rằng đó là một ý kiến ​​hay nếu bạn đưa một phụ thuộc vào để giải quyết vấn đề một cách không cần thiết.
Rupert Madden-Abbott

19

Câu trả lời từ @Rupert Madden-Abbott hoạt động. Tuy nhiên, nếu bạn có một hàm tạo không phải mặc định, ví dụ:

Builder(String city, String country) {...}

Sau đó, bạn nên chú thích các thông số như bên dưới:

@JsonCreator
Builder(@JsonProperty("city")    String city, 
        @JsonProperty("country") String country) {...}

9

Một giải pháp phù hợp với tôi trong trường hợp này (tôi đã sử dụng chú thích trình tạo "Lombok").

@Getter
@Builder(builderMethodName = "builder")
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@JsonAutoDetect(
    fieldVisibility = JsonAutoDetect.Visibility.ANY,
    creatorVisibility = JsonAutoDetect.Visibility.ANY
)

Tôi hy vọng cũng sẽ hữu ích cho bạn.


Điều này hiện đã lỗi thời, với Lombok 1.18.4, bạn có thể sử dụng @Jacksonizedđể thay thế trình tạo bên trong và chú thích jackson bằng một thứ duy nhất
Randakar

7

Tôi đã kết thúc việc triển khai điều này bằng cách sử dụng @JsonDeserialize như sau:

@JsonDeserialize(using = JacksonDeserializer.class)
public class Address
{...}

@JsonCachable
static class JacksonDeserializer extends JsonDeserializer<Address>
{
    @Override
    public Address deserialize(JsonParser parser, DeserializationContext context)
        throws IOException, JsonProcessingException
    {
        JsonToken token = parser.getCurrentToken();
        if (token != JsonToken.START_OBJECT)
        {
            throw new JsonMappingException("Expected START_OBJECT: " + token, parser.getCurrentLocation());
        }
        token = parser.nextToken();
        Builder result = new Builder();
        while (token != JsonToken.END_OBJECT)
        {
            if (token != JsonToken.FIELD_NAME)
            {
                throw new JsonMappingException("Expected FIELD_NAME: " + token, parser.getCurrentLocation());
            }
            LocationType key = LocationType.valueOf(parser.getText());

            token = parser.nextToken();
            if (token != JsonToken.VALUE_STRING)
            {
                throw new JsonMappingException("Expected VALUE_STRING: " + token, parser.getCurrentLocation());
            }
            String value = parser.getText();

            // Our Builder allows passing key-value pairs
            // alongside the normal setter methods.
            result.put(key, value);
            token = parser.nextToken();
        }
        return result.create();
    }
}

Đây có thể là cách bạn đã thực hiện nó, nhưng câu trả lời này không thực sự trả lời câu hỏi như đã đặt ra. Câu trả lời do @Rupert Madden-Abbott đăng phải được đánh dấu là câu được chấp nhận.
kelnos 27/02/19

2

Hiện tại không có hỗ trợ nào cho mẫu trình tạo, mặc dù nó đã được yêu cầu cách đây khá lâu (và cuối cùng, vấn đề Jira http://jira.codehaus.org/browse/JACKSON-469 đã được gửi) - đó là thứ có thể được thêm vào cho bản phát hành 1.8 nếu đủ nhu cầu (đảm bảo bỏ phiếu tại Jira!). Đây là một tính năng bổ sung hợp lý và chỉ bị trì hoãn theo khoảng thời gian mà các nhà phát triển có. Nhưng tôi nghĩ nó sẽ là một bổ sung tuyệt vời.


2
Codehaus không còn có Jira sẵn nhưng vấn đề liên quan được mô tả ở đây: wiki.fasterxml.com/JacksonFeatureBuilderPattern
Paul

Hỗ trợ cho mẫu Builder đã được thêm vào từ lâu, giống như Jackson 2.2.
StaxMan

2

Điều này phù hợp với tôi: @NoArgsConstructor Hạn chế duy nhất của điều này là người ta có thể làm = new ADTO () một lần nữa. Nhưng, này, dù sao thì tôi cũng không thích de code Police, nói cho tôi biết cách sử dụng mã someones :-) Vì vậy, hãy sử dụng POJO DTOS của tôi theo cách bạn thích. Có hoặc không có trình xây dựng. Tôi đề nghị: hãy làm điều đó với Người xây dựng, nhưng hãy là khách của tôi ...

@Data
@Builder
//Dont forget this! Otherwise no Jackson serialisation possible!
@NoArgsConstructor
@AllArgsConstructor
public class ADTO {
.....
}

Bạn không muốn biết cách sử dụng mã của ai đó?
masterxilo
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.