2013-01-22 4 views
0

Comment puis-je demander à jackson d'utiliser un sérialiseur différent pour chaque type de collection générique. J'ai un CustomProductsSerializer et un CustomClientsSerializerSérialisation polymorphe des collections avec Custom Serializer dans jackson

public class ProductsListSerializer extends JsonSerializer<List<Products>>{ 

    // logic to insert __metadata , __count into json 
    public void serialize(List<Products> value, JsonGenerator jgen, SerializerProvider provider) { 
    .... .... .... 
    } 
    // always returns ArrayList.class 
    public Class<List<Instrument>> handledType() { 
    Class c = var.getClass(); 
    return c; 
    } 
    } 


    public class CustomClientsSerializer extends JsonSerializer<List<Clients>>{ 

    // logic to insert __metadata , __count into json 
    public void serialize(List<Clients> value, JsonGenerator jgen, SerializerProvider provider){ 
    .... .... .... 
    } 
    .... .... .... 
    // always returns ArrayList.class 
    public Class<List<Clients>> handledType() { 
    Class c = var.getClass(); 
    return c; 
    } 
    } 

Je me suis inscrit à la fois les serializers utilisant SimpleModule. Le problème étant que handleType dans les deux cas renvoie ArrayList.class, la sérialisation des clients ci-dessous échoue avec une exception de transtypage de classe.

List<Clients> clients; 
    // code to get clients from database. 
    String jsonString = mapper.writeValueAsString(clients); 

Comment demander à jackson quel sérialiseur utiliser? J'utilise jackson comme sérialiseur json avec resteasy.

--Modifier en réponse à @ HiJon89 J'ai un objet métier qui renvoie 'Liste' et un autre qui renvoie Liste. La façon dont j'ai besoin est quand l'objet Busines retourne une 'Liste' alors ProductsListSerializer devrait être utilisé pour toute la liste. et Lorsque l'objet métier renvoie la liste, le CustomClientsSerializer doit être utilisé pour la liste entière. Dans mon cas d'utilisation, j'ajoute des éléments supplémentaires au json sérialisé 'eg: __ count, __metadata, __references' Il n'y a qu'un seul compte par collection. Contentusing Ne peut être utilisé que sur les propriétés. Aucune suggestion ?

+0

Le problème peut être résolu en enregistrant le sérialiseur pour un JavaType donné (com.fasterxml.jackson.databind). Mais le simpleModule.addSerializer() accepte deux paramètres Class et Serilaizer. Je ne suis pas en mesure de trouver une méthode qui peut accepter JavaType. –

Répondre

0

Il est possible en étendant le module simplem ou en étendant com.fasterxml.jackson.databind.Module. J'ai pris la deuxième approche. Voici le résumé des modifications

méthode ajoutée pour prendre JavaType en tant que paramètre.

public <T> TypedModule addSerializer(JavaType type, JsonSerializer<T> ser) 
{ 
    if (_serializers == null) { 
     _serializers = new TypedSerializers(); 
    } 
    _serializers.addSerializer(type, ser); 
    return this; 
} 

méthode ci-dessus ajoute la nouvelle serilaizer à

protected TypedSerializers _serializers = null; 

La classe « étend TypedSerializers Sérialiseurs.Base 'stocke tous les sérialiseurs typés dans une HashMap.

Changé la logique de la méthode findSerializer dans TypedSerializers

public JsonSerializer<?> findSerializer(SerializationConfig config, 
     JavaType type, BeanDescription beanDesc) { 

    Class<?> cls = type.getRawClass(); 
    ClassKey key = new ClassKey(cls); 
    JsonSerializer<?> ser = null; 
    if (_javaTypeMappings != null) { 
     ser = _javaTypeMappings.get(type.toString()); 
     if (ser != null) { 
      return ser; 
     } 
    } 
..... process normal serilaizers ... 

Ci-dessous le changement est nécessaire lors de l'enregistrement d'un customserializer

TypedModule simpleModule = new TypedModule("TypedModule", new Version(1, 0, 0, null));  
    JavaType productlist = this.getTypeFactory().constructCollectionType(List.class, Product.class); 
    JavaType clientlist = this.getTypeFactory().constructCollectionType(List.class, Client.class); 
    simpleModule.addSerializer(productlist, new prodctlistserializer()); 
    simpleModule.addSerializer(clientlist, new clientlistserializer()); 
    this.registerModule(simpleModule); 

Le comportement qui en résulte est, quand « Liste des type de produit » est passé à ObjectMapper , il essaye de localiser un serilaizer approprié. Nous maintenons les sérialiseurs génériques dans la carte de _javaTypeMappings. Objecmapper exécute findSerializer. FindSerializer recherche d'abord un sérialiseur disponible dans _javaTypeMappings. Dans ce cas, il renvoie prodctlistserializer. Lorsque la 'Liste de type Client' est passée, elle renvoie clientlistserializer.

J'ai pris tout le code source sous com.fasterxml.jackson.databind.Module à un paquet dans mon projet et ajouté les changements ci-dessus.

3

Voulez-vous réellement que la sérialisation/désérialisation de ce type soit gérée différemment quand elle se trouve dans une collection? Sinon, vous pouvez simplement annoter la classe Product avec quelque chose comme: @JsonSerialize(using = ProductSerializer.class) et Jackson utilisera votre sérialiseur chaque fois qu'il rencontre un Product (y compris à l'intérieur des collections).

Si vous avez réellement besoin d'une manipulation différente à l'intérieur d'une collection, vous pouvez annoter la classe avec quelque chose comme: @JsonSerialize(using = ProductSerializer.class, contentUsing = ProductInCollectionSerializer.class). Cela fera la même chose que ci-dessus, sauf que ProductInCollectionSerializer sera utilisé pour la sérialisation à la place lorsque Jackson rencontre une collection contenant Product.

EDIT basé sur commentaire:

Jackson sérialisera une collection comme un tableau JSON donc il n'y a pas de place pour mettre ces propriétés supplémentaires. Il semble que vous souhaitiez que Jackson renvoie un objet JSON contenant un tableau JSON ainsi que des métadonnées supplémentaires. Dans ce cas, la meilleure approche est envelopper le List<Product> dans un objet tel que:

public class ProductListWrapper { 
    private final List<Product> products; 

    public ProductListWrapper(List<Product> products) { 
     this.products = products; 
    } 

    public List<Product> getProducts() { 
     return products; 
    } 

    @JsonProperty("_count") 
    public int getCount() { 
     return getProducts() == null ? 0 : getProducts().size(); 
    } 
} 

Jackson peut alors sérialiser cet objet sans qu'il soit nécessaire d'écrire des serializers personnalisés et produira JSON ressembler à:

{ 
    "products": [....] 
    "_count": 25 
} 
+0

J'ai un objet de gestion qui renvoie «Liste » et un autre qui renvoie la liste . La façon dont j'ai besoin est quand l'objet Busines retourne une 'Liste ' alors ProductsListSerializer devrait être utilisé pour toute la liste. Lorsque l'objet métier renvoie la liste , CustomClientsSerializer doit être utilisé pour la liste entière. Dans mon cas d'utilisation, j'ajoute des éléments supplémentaires au json sérialisé 'eg: __ count, __metadata, __references' Il n'y a qu'un seul compte par collection. Contentusing Ne peut être utilisé que sur les propriétés. Aucune suggestion ? –

+0

basé sur votre édition .. J'ai été explorant sérialiseur personnalisé pour éviter l'objet wrapper. Avec les objets wrapper, le modèle de domaine nécessite une modification. Y at-il un moyen d'éviter de polluer le modèle de domaine? –

+0

Je ne peux pas penser à un moyen facile de faire ce que vous voulez sans faire quelque chose d'extrême comme ajouter des annotations mix-à List.class. Si vous n'emballez pas la liste, où votre sérialiseur obtiendra-t-il les métadonnées? (en plus des choses comme '_count' qui peuvent être trouvées dans la liste) Je préfère personnaliser le moins possible la représentation JSON et utiliser des annotations pour les personnaliser autant que possible afin que les gens puissent voir l'objet qu'ils ont attendre. Si vous commencez à écrire des sérialiseurs pour différents types de listes, comment quelqu'un peut-il savoir à quoi s'attendre JSON? – HiJon89

Questions connexes