Jackson + Builder Pattern?


89

Я хотів би, щоб Джексон десеріалізував клас за допомогою такого конструктора:

public Clinic(String name, Address address)

Десериалізувати перший аргумент легко. Проблема в тому, що Адреса визначається як:

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

і будується так: new Address.Builder().setCity("foo").setCountry("bar").create();

Чи є спосіб отримати пари ключ-значення від Джексона, щоб самостійно побудувати Адресу? Як варіант, чи є спосіб змусити Джексона використовувати сам клас Builder?

Відповіді:


139

Поки ви використовуєте Jackson 2+, тепер для цього вбудована підтримка .

Спочатку потрібно додати цю анотацію до свого Addressкласу:

@JsonDeserialize(builder = Address.Builder.class)

Потім вам потрібно додати цю анотацію до свого Builderкласу:

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

Ви можете пропустити цю другу анотацію, якщо із задоволенням перейменуєте метод створення свого Builder для створення, а сетери вашого Builder повинні мати префікс, а не set.

Повний приклад:

@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
Якщо потрібно позбутися від @JsonPOJOBuilderанотації разом, перейменуйте "create" на "build" і коментуйте кожен із установників конструктора @JsonProperty.
Сем Беррі

це золото. Дякую.
Mukul Goel

Зараз це застаріло, ви можете використовувати Lombok 1.18.4, @Jacksonizedякий замінює внутрішній
конструктор

@Randakar Я не думаю, що це застаріло, оскільки а) @Jackonized - це щойно випущена експериментальна функція в Ломбоці. Я не думаю, що це непогана ідея заохочувати використання експериментальних функцій. б) у питанні не згадується і не використовується Ломбок. Я не думаю, що це непогана ідея вводити залежність для вирішення проблеми.
Руперт Медден-Ебботт

19

Відповідь від @Rupert Madden-Abbott працює. Однак, якщо у вас є конструктор, який не є типовим, наприклад,

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

Тоді ви повинні анотувати параметри, як показано нижче:

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

9

Рішення, яке мені підходило в даному випадку (я використовував анотацію конструктора "Ломбок").

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

Я сподіваюся, це буде корисним і для вас.


Зараз це застаріло, ви можете використовувати Lombok 1.18.4, @Jacksonizedякий замінює внутрішній
конструктор

7

У підсумку я реалізував це за допомогою @JsonDeserialize наступним чином:

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

Можливо, саме так ви і закінчили його реалізовувати, але ця відповідь насправді не відповідає на поставлене запитання. Відповідь, опубліковану @Rupert Madden-Abbott, повинна бути позначена як прийнята.
kelnos

2

В даний час не існує підтримки для шаблону будівельника, хоча про це вже подавались досить давно (і нарешті було видано випуск Jira http://jira.codehaus.org/browse/JACKSON-469 ) - це те, що можна додати за випуск 1.8, якщо буде достатньо попиту (обов’язково проголосуйте за Jira!). Це розумна додаткова функція, яка затримується лише на стільки часу, скільки у розробників. Але я думаю, що це було б чудовим доповненням.


2
Codehaus більше не має Jira, але тут описана пов’язана проблема: wiki.fasterxml.com/JacksonFeatureBuilderPattern
Пол

Підтримка шаблону Builder давно додана, наприклад, на зразок Jackson 2.2.
StaxMan

2

Це спрацювало для мене: @NoArgsConstructor Єдиним недоліком цього є те, що знову можна зробити = new ADTO (). Але, привіт, мені так чи інакше не подобається поліція de code, яка говорить мені, як користуватися чиїмось кодом :-) Отже, використовуй мою POJO DTOS так, як тобі подобається. З будівельником або без нього. Я пропоную: зробіть це з будівельником, але будьте моїм гостем ...

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

Вам не подобається знати, як користуватися чиїмсь кодом?
masterxilo
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.