120 votes

Comment utiliser un sérialiseur personnalisé avec Jackson?

J'ai deux classes Java que je veux pour sérialiser en JSON à l'aide de Jackson:

public class User {
    public final int id;
    public final String name;

    public User(int id, String name) {
        this.id = id;
        this.name = name;
    }
}

public class Item {
    public final int id;
    public final String itemNr;
    public final User createdBy;

    public Item(int id, String itemNr, User createdBy) {
        this.id = id;
        this.itemNr = itemNr;
        this.createdBy = createdBy;
    }
}

Je veux sérialiser un Élément à cette JSON:

{"id":7, "itemNr":"TEST", "createdBy":3}

avec de l'Utilisateur sérialisé pour inclure uniquement l' id. Je vais également être en mesure de serilize tous les objets utilisateur en JSON comme:

{"id":3, "name": "Jonas", "email": "jonas@example.com"}

Donc je suppose que j'ai besoin d'écrire une coutume sérialiseur pour Item et essayé avec ceci:

public class ItemSerializer extends JsonSerializer<Item> {

@Override
public void serialize(Item value, JsonGenerator jgen,
        SerializerProvider provider) throws IOException,
        JsonProcessingException {
    jgen.writeStartObject();
    jgen.writeNumberField("id", value.id);
    jgen.writeNumberField("itemNr", value.itemNr);
    jgen.writeNumberField("createdBy", value.user.id);
    jgen.writeEndObject();
}

}

Je sérialiser le JSON avec ce code de Jackson Comment: Personnalisé Sérialiseurs:

ObjectMapper mapper = new ObjectMapper();
SimpleModule simpleModule = new SimpleModule("SimpleModule", 
                                              new Version(1,0,0,null));
simpleModule.addSerializer(new ItemSerializer());
mapper.registerModule(simpleModule);
StringWriter writer = new StringWriter();
try {
    mapper.writeValue(writer, myItem);
} catch (JsonGenerationException e) {
    e.printStackTrace();
} catch (JsonMappingException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}

Mais j'ai cette erreur:

Exception in thread "main" java.lang.IllegalArgumentException: JsonSerializer of type com.example.ItemSerializer does not define valid handledType() (use alternative registration method?)
    at org.codehaus.jackson.map.module.SimpleSerializers.addSerializer(SimpleSerializers.java:62)
    at org.codehaus.jackson.map.module.SimpleModule.addSerializer(SimpleModule.java:54)
    at com.example.JsonTest.main(JsonTest.java:54)

Comment puis-je utiliser un Sérialiseur avec Jackson?


C'est la façon dont je le ferais avec Gson:

public class UserAdapter implements JsonSerializer<User> {

    @Override 
    public JsonElement serialize(User src, java.lang.reflect.Type typeOfSrc,
            JsonSerializationContext context) {
        return new JsonPrimitive(src.id);
    }
}

    GsonBuilder builder = new GsonBuilder();
    builder.registerTypeAdapter(User.class, new UserAdapter());
    Gson gson = builder.create();
    String json = gson.toJson(myItem);
    System.out.println("JSON: "+json);

Mais j'ai besoin de le faire avec Jackson maintenant, depuis Gson n'est pas prise en charge pour les interfaces.

65voto

Moesio Points 788

Vous pouvez placer @JsonSerialize(using = CustomDateSerializer.class) sur n'importe quel champ de date de l'objet à sérialiser.

 public class CustomDateSerializer extends SerializerBase<Date> {

public CustomDateSerializer() {
    super(Date.class, true);
}

@Override
public void serialize(Date value, JsonGenerator jgen, SerializerProvider provider)
        throws IOException, JsonGenerationException {
    SimpleDateFormat formatter = new SimpleDateFormat("EEE MMM dd yyyy HH:mm:ss 'GMT'ZZZ (z)");
    String format = formatter.format(value);
    jgen.writeString(format);
}

}
 

51voto

StaxMan Points 34626

Comme mentionné, @JsonValue est un bon moyen. Mais si vous n'avez pas l'esprit un personnalisé sérialiseur, il n'y a pas besoin d'en écrire un pour l'Article, mais plutôt pour l'Utilisateur -- si oui, il serait aussi simple que:

public void serialize(Item value, JsonGenerator jgen,
    SerializerProvider provider) throws IOException,
    JsonProcessingException {
  jgen.writeNumber(id);
}

Encore une autre possibilité est de mettre en oeuvre JsonSerializable, auquel cas aucune inscription n'est nécessaire.

Comme à l'erreur; c'est bizarre -- vous voulez probablement mettre à niveau vers une version ultérieure. Mais il est également plus sûr pour prolonger org.codehaus.jackson.map.ser.SerializerBase qu'il aura mise en œuvre standard de non-essentiels des méthodes (c'est à dire tout sauf réel de la sérialisation d'appel).

38voto

pmhargis Points 113

J'ai essayé de le faire trop, et il y a une erreur dans l'exemple de code sur le Jackson page web qui ne parvient pas à comprendre le type (.classe) dans l'appel à addSerializer méthode, qui devrait se lire comme ceci:

simpleModule.addSerializer(Item.class, new ItemSerializer());

En d'autres termes, ce sont les lignes qui instancient les simpleModule et ajouter le sérialiseur (avec l'état de la ligne incorrecte commenté):

ObjectMapper mapper = new ObjectMapper();
SimpleModule simpleModule = new SimpleModule("SimpleModule", 
                                          new Version(1,0,0,null));
// simpleModule.addSerializer(new ItemSerializer());
simpleModule.addSerializer(Item.class, new ItemSerializer());
mapper.registerModule(simpleModule);

Pour info: Voici la référence pour le bon exemple de code: http://wiki.fasterxml.com/JacksonFeatureModules

Espérons que cette aide!

10voto

henrik_lundgren Points 1646

Utilisez @JsonValue:

 public class User {
    int id;
    String name;

    @JsonValue
    public int getId() {
        return id;
    }
}
 

@JsonValue ne fonctionne que sur les méthodes, vous devez donc ajouter la méthode getId. Vous devriez pouvoir ignorer votre sérialiseur personnalisé.

6voto

user430904 Points 131

Jackson JSON points de Vue peut-être un moyen plus simple de la réalisation de vos exigences, surtout si vous avez une certaine souplesse dans votre format JSON.

Si {"id":7, "itemNr":"TEST", "createdBy":{id:3}} est une représentation acceptable, puis ce sera très facile à réaliser avec très peu de code.

Vous annoter le nom de domaine de l'Utilisateur comme étant une partie de la vue, et de spécifier un point de vue différent dans votre sérialisation demande (onu-annoté champs devraient être inclus par défaut)

Par exemple: Définir les points de vue:

public class Views {
    public static class BasicView{}
    public static class CompleteUserView{}
}

Annoter l'Utilisateur:

public class User {
    public final int id;

    @JsonView(Views.CompleteUserView.class)
    public final String name;

    public User(int id, String name) {
        this.id = id;
        this.name = name;
    }
}

Et serialise demande d'un point de vue qui ne contient pas le champ que vous souhaitez masquer (non annotée champs sont sérialisés par défaut):

objectMapper.getSerializationConfig().withView(Views.BasicView.class);

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X