Jackson + Patrón Constructor?


Me gustaría Jackson deserializar una clase con el siguiente constructor:

public Clinic(String name, Address address)

Deserializar el primer argumento es fácil. El problema es que la Dirección se define como:

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

Y se construye así: new Address.Builder().setCity("foo").setCountry("bar").create();

¿Hay alguna manera de obtener pares clave-valor de Jackson para construir la Dirección yo mismo? Alternativamente, ¿hay alguna manera de hacer que Jackson use la clase Builder en sí?

Author: Vlasec, 2011-02-13

5 answers

Mientras esté usando Jackson 2+, entonces ahora hay construido en soporte para esto.

Primero debe agregar esta anotación a su clase Address:

@JsonDeserialize(builder = Address.Builder.class)

Entonces necesita agregar esta anotación a su clase Builder:

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

Puede omitir esta segunda anotación si está dispuesto a cambiar el nombre del método create de su Constructor para construir, y los configuradores de su Constructor para ser prefijados con, en lugar de establecer.

Ejemplo completo:

@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();
  }
}
 80
Author: Rupert Madden-Abbott,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-09 12:24:16

La respuesta de @Rupert Madden-Abbott funciona. Sin embargo, si tiene un constructor no predeterminado, por ejemplo,

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

Entonces debe anotar los parámetros de la siguiente manera:

@JsonCreator
Builder(@JsonProperty("city")    String city, 
        @JsonProperty("country") String country) {...}
 19
Author: volatilevar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-08-01 17:35:17

Terminé implementando esto usando @JsonDeserialize de la siguiente manera:

@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();
    }
}
 8
Author: Gili,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-02-14 03:53:36

Una solución que era adecuada para mí en este caso (usé la anotación "Lombok" builder).

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

Espero que sea útil para u también.

 3
Author: JustK K,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-06 07:32:38

Actualmente no hay soporte para builder pattern, aunque se ha solicitado hace bastante tiempo (y finalmente Jira issue http://jira.codehaus.org/browse/JACKSON-469 se presentó) is es algo que se puede agregar para la versión 1.8 si hay suficiente demanda (asegúrese de votar en Jira!). Es una característica adicional razonable, y solo se retrasa por la cantidad de tiempo que los desarrolladores tienen. Pero creo que sería una gran adición.

 2
Author: StaxMan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-02-13 07:21:36