Jackson + Pola Pembangun?

89

Saya ingin Jackson melakukan deserialisasi kelas dengan konstruktor berikut:

public Clinic(String name, Address address)

Deserialisasi argumen pertama itu mudah. Masalahnya adalah bahwa Alamat didefinisikan sebagai:

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

dan dibangun seperti ini: new Address.Builder().setCity("foo").setCountry("bar").create();

Apakah ada cara untuk mendapatkan pasangan nilai-kunci dari Jackson untuk membuat Alamat sendiri? Atau, adakah cara agar Jackson menggunakan kelas Builder itu sendiri?

Gili
sumber

Jawaban:

139

Selama Anda menggunakan Jackson 2+, maka sekarang sudah ada dukungan bawaan untuk ini .

Pertama, Anda perlu menambahkan anotasi ini ke Addresskelas Anda :

@JsonDeserialize(builder = Address.Builder.class)

Kemudian Anda perlu menambahkan anotasi ini ke Builderkelas Anda :

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

Anda dapat melewati anotasi kedua ini jika Anda ingin mengganti nama metode create Builder Anda menjadi build, dan setter Builder Anda menjadi awalan dengan, bukan set.

Contoh lengkap:

@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();
  }
}
Rupert Madden-Abbott
sumber
14
Jika Anda ingin menyingkirkan @JsonPOJOBuildersemua anotasi secara bersamaan, ganti nama "create" menjadi "build" dan beri anotasi pada setiap penyetel pembuat @JsonProperty.
Sam Berry
ini emas. Terima kasih.
Mukul Goel
Ini sekarang sudah usang, dengan Lombok 1.18.4 yang dapat Anda gunakan @Jacksonizedyang menggantikan pembangun dalam dan anotasi jackson dengan satu hal
Randakar
@Randakar Saya rasa ini tidak ketinggalan jaman karena a) @Jackonized adalah fitur eksperimental yang baru saja dirilis di Lombok. Menurut saya, tidak perlu mendorong penggunaan fitur eksperimental secara tidak perlu. b) pertanyaan tidak menyebutkan atau menggunakan Lombok. Saya tidak berpikir itu adalah ide yang baik untuk memperkenalkan ketergantungan yang tidak perlu untuk memecahkan masalah.
Rupert Madden-Abbott
19

Jawaban dari @Rupert Madden-Abbott berhasil. Namun, jika Anda memiliki konstruktor non-default, misalnya,

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

Maka Anda harus membuat anotasi parameter seperti di bawah ini:

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

Sebuah solusi yang cocok untuk saya dalam kasus ini (saya menggunakan anotasi pembuat "Lombok").

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

Saya harap akan berguna juga untuk Anda.

JustK K
sumber
Ini sekarang sudah usang, dengan Lombok 1.18.4 yang dapat Anda gunakan @Jacksonizedyang menggantikan pembangun dalam dan anotasi jackson dengan satu hal
Randakar
7

Saya akhirnya menerapkan ini menggunakan @JsonDeserialize sebagai berikut:

@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();
    }
}
Gili
sumber
Ini mungkin bagaimana Anda akhirnya menerapkannya, tetapi jawaban ini tidak benar-benar menjawab pertanyaan seperti yang diajukan. Jawaban yang diposting oleh @Rupert Madden-Abbott harus ditandai sebagai jawaban yang diterima.
kelnos
2

Saat ini tidak ada dukungan untuk pola pembangun, meskipun itu telah diminta beberapa waktu yang lalu (dan akhirnya masalah Jira http://jira.codehaus.org/browse/JACKSON-469 diajukan) - itu adalah sesuatu yang dapat ditambahkan untuk rilis 1.8 jika permintaan cukup (pastikan untuk memberikan suara di Jira!). Ini adalah fitur tambahan yang masuk akal, dan hanya tertunda oleh waktu yang dimiliki pengembang. Tapi saya pikir itu akan menjadi tambahan yang bagus.

StaxMan
sumber
2
Codehaus tidak lagi memiliki Jira tersedia tetapi masalah terkait dijelaskan di sini: wiki.fasterxml.com/JacksonFeatureBuilderPattern
Paul
Dukungan untuk pola Builder telah lama ditambahkan, seperti Jackson 2.2.
StaxMan
2

Ini bekerja untuk saya: @NoArgsConstructor Satu-satunya kelemahan dari ini, adalah bahwa seseorang dapat melakukan = new ADTO () lagi. Tapi, hei, saya tidak suka de code police, memberi tahu saya cara menggunakan kode seseorang :-) Jadi, gunakan POJO DTOS saya sesuka Anda. Dengan atau tanpa pembangun. Saya sarankan: lakukan dengan Builder, tapi jadilah tamuku ...

@Data
@Builder
//Dont forget this! Otherwise no Jackson serialisation possible!
@NoArgsConstructor
@AllArgsConstructor
public class ADTO {
.....
}
Roland Roos
sumber
Anda tidak ingin tahu cara menggunakan kode seseorang?
masterxilo