web-dev-qa-db-fra.com

Modèle Jackson + Builder?

J'aimerais que Jackson désérialise une classe avec le constructeur suivant:

public Clinic(String name, Address address)

La désérialisation du premier argument est facile. Le problème est que l'adresse est définie comme:

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

et est construit comme ceci: new Address.Builder().setCity("foo").setCountry("bar").create();

Existe-t-il un moyen d'obtenir des paires clé-valeur de Jackson afin de construire l'adresse moi-même? Sinon, existe-t-il un moyen pour que Jackson utilise la classe Builder elle-même?

72
Gili

Tant que vous utilisez Jackson 2+, il y a maintenant support intégré pour cela .

Vous devez d'abord ajouter cette annotation à votre classe Address:

@JsonDeserialize(builder = Address.Builder.class)

Ensuite, vous devez ajouter cette annotation à votre classe Builder:

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

Vous pouvez ignorer cette deuxième annotation si vous êtes heureux de renommer la méthode de création de votre générateur en génération, et les setters de votre générateur avec le préfixe avec, au lieu de définir.

Exemple complet:

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

La réponse de @Rupert Madden-Abbott fonctionne. Cependant, si vous avez un constructeur non par défaut, par exemple,

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

Ensuite, vous devez annoter les paramètres comme ci-dessous:

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

J'ai fini par implémenter cela en utilisant @JsonDeserialize comme suit:

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

Une solution qui me convenait dans ce cas (j'ai utilisé l'annotation de constructeur "Lombok").

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

J'espère que ce serait utile pour vous aussi.

5
JustK K

Il n'y a actuellement aucun support pour le modèle de générateur, bien qu'il ait été demandé il y a longtemps (et enfin le problème Jira http://jira.codehaus.org/browse/JACKSON-469 a été déposé) - c'est quelque chose qui peut être ajouté pour la version 1.8 si la demande est suffisante (assurez-vous de voter à Jira!). C'est une fonctionnalité supplémentaire raisonnable, et seulement retardée par le temps dont disposent les développeurs. Mais je pense que ce serait un excellent ajout.

3
StaxMan