2011-02-13 3 views
39

Je voudrais Jackson désérialiser une classe avec le constructeur suivant:Modèle Jackson + Builder?

public Clinic(String name, Address address) 

désérialisation le 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();

Y at-il un moyen d'obtenir des paires clé-valeur de Jackson pour construire l'adresse moi-même? Alternativement, y a-t-il un moyen d'amener Jackson à utiliser la classe Builder elle-même?

Répondre

6

Je fini par mettre en œuvre cette aide de la @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(); 
    } 
} 
2

Actuellement, il n'y a pas de support pour le pattern builder, bien qu'il ait été demandé il y a longtemps (et enfin le numéro http://jira.codehaus.org/browse/JACKSON-469 de Jira a été ajouté) - c'est quelque chose qui peut être ajouté pour la version 1.8 si la demande est suffisante. sûr de voter à Jira!). C'est une fonctionnalité supplémentaire raisonnable, et seulement retardée par la quantité de temps que les développeurs ont. Mais je pense que ce serait un excellent ajout.

+1

Codehaus n'a plus Jira disponible mais le problème lié est décrit ici: http://wiki.fasterxml.com/JacksonFeatureBuilderPattern – Paul

+0

La prise en charge du modèle Builder a été ajoutée depuis longtemps, à quelque chose comme Jackson 2.2. – StaxMan

66

Tant que vous utilisez Jackson 2+, alors il est maintenant built in support for this.

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

@JsonDeserialize(builder = Address.Builder.class) 

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

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

Vous pouvez sauter cette seconde annotation si vous sont heureux de renommer la méthode create de votre Builder pour construire, et les setters de votre Builder d'être préfixés 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(); 
    } 
} 
+0

Merci pour la mise à jour! – Gili

+8

Si vous souhaitez supprimer l'annotation '@ JsonPOJOBuilder', renommez" create "en" build "et annotez chacun des setters du constructeur avec' @ JsonProperty'. –

+0

c'est or. Merci. –

15

La réponse de Madden fonctionne @Rupert-Abbott. Toutefois, si vous avez un constructeur 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) {...} 
0

Une solution qui me convenait dans ce cas (je " Lombok "annotation du constructeur").

@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.