2014-05-12 4 views
9

Est-il possible de configurer Jersey en utilisant Jackson pour la sérialisation/désérialisation en utilisant plusieurs configurations ObjectMappers? Ce que je voudrais être en mesure de faire est d'enregistrer un "par défaut" Jackson ObjectMapper et ensuite avoir la possibilité d'enregistrer une autre caractéristique qui fournit un ObjectMapper avec une configuration spécialisée qui, dans certains cas, "remplacera" le "par défaut" ObjectMapper.Utilisation de Jackson dans Jersey avec plusieurs ObjectMappers configurés

Par exemple, ce ContextResolver serait le "défaut" Mapper:

@Provider 
@Consumes(MediaType.APPLICATION_JSON) 
@Produces(MediaType.APPLICATION_JSON) 
public class JacksonMapperProvider implements ContextResolver<ObjectMapper> { 
    private final ObjectMapper mObjectMapper; 

    public JacksonMapperProvider() { 
     mObjectMapper = createMapper(); 
    } 

    protected abstract ObjectMapper createMapper() { 
     ObjectMapper mapper = createMapper(); 

     return mapper 
      .setSerializationInclusion(Include.ALWAYS) 
      .configure(JsonParser.Feature.ALLOW_COMMENTS, true) 
      .configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true) 
      .configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true) 
      .configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true); 
    } 

    @Override 
    public ObjectMapper getContext(Class<?> type) { 
     return mObjectMapper; 
    } 
} 

Et ce ContextResolver serait de remplacer le "défaut" Mapper:

@Provider 
@Consumes(MediaType.APPLICATION_JSON) 
@Produces(MediaType.APPLICATION_JSON) 
public class SpecializedMapperProvider implements ContextResolver<ObjectMapper> { 
    private final ObjectMapper mObjectMapper; 

    public SpecializedMapperProvider() { 
     mObjectMapper = createMapper(); 
    } 

    protected abstract ObjectMapper createMapper() { 
     ObjectMapper mapper = createMapper(); 

     return mapper 
      .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) 
      .setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS")) 
      .registerModule(new SpecializedModule1()) 
      .registerModule(new SpecializedModule2()); 
    } 

    @Override 
    public ObjectMapper getContext(Class<?> type) { 
     if(SomeType.isAssignableFrom(type)) { 
      return mObjectMapper; 
     } 
     return null; 
    } 
} 

Je vois dans le JacksonJsonProvider code que Jackson prend en charge ObjectMapper injection/résolution du fournisseur. Cependant, en pratique, ce que je vois, c'est que «l'ordre» des fournisseurs semble aléatoire (je suppose que ce n'est pas le cas, mais je n'arrive pas à déterminer comment contrôler la commande). Parfois, le "override" vient avant le "default" et tout fonctionne, mais au prochain démarrage du serveur, l'ordre change.

J'ai essayé d'obtenir ce à travailler dans un certain nombre de façons, notamment:

  • Enregistrement des ContextResolver<ObjectMapper> mises en œuvre manuellement (des commandes différentes)
  • Enregistrement des ContextResolver<ObjectMapper> implémentations via @Provider annotations
  • Définition d'un priorité lors de l'inscription

J'utilise ce qui suit:

  • Jersey 2,8
  • Jackson 2.3.3

Peut-être que je prend une approche complètement incorrecte?
Existe-t-il un meilleur moyen de réaliser ce que j'essaie de faire? Peut-être devrais-je simplement définir deux applications JAX-RS distinctes et avoir une seule configuration ObjectMapper pour chacune?

+0

Ce que je trouve est une fois que vous avez 'ObjectMapper defaultMapper = return new ObjectMapper()', ce n'est pas la même chose que l'instance par défaut de 'JacksonJsonProvider'. Atleast pas pour 'JacksonJaxbJsonProvider'. – ulab

Répondre

2

Vous pouvez configurer l'ordre des fournisseurs, mais il serait en fait préférable d'utiliser un fournisseur dans cette situation:

@Provider 
public class JacksonMapperProvider implements ContextResolver<ObjectMapper> { 
    private final ObjectMapper defaultMapper; 
    private final ObjectMapper specializedMapper; 

    public JacksonMapperProvider() { 
     defaultMapper = createDefaultMapper(); 
     specializedMapper = createSpecializedMapper(); 
    } 

    private static ObjectMapper createDefaultMapper() { 
     return new ObjectMapper() 
      .setSerializationInclusion(Include.ALWAYS) 
      .configure(JsonParser.Feature.ALLOW_COMMENTS, true) 
      .configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true) 
      .configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true) 
      .configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true); 
    } 

    private static ObjectMapper createSpecializedMapper() { 
     return new ObjectMapper() 
      .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) 
      .setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS")) 
      .registerModule(new SpecializedModule1()) 
      .registerModule(new SpecializedModule2()); 
    } 

    @Override 
    public ObjectMapper getContext(Class<?> type) { 
     if (SomeType.isAssignableFrom(type)) { 
      return specializedMapper; 
     } 
     else { 
      return defaultMapper; 
     } 
    } 
} 
+2

Merci. C'est ce que j'ai fait initialement pour le faire fonctionner, cependant, j'espérais garder les choses séparées et agnostiques des différents modules/domaines de l'application et éviter un "méga-fournisseur" potentiel qui pourrait résulter de différents mappeurs nécessaires. Pouvez-vous expliquer pourquoi "il serait préférable d'utiliser un fournisseur dans cette situation"? –

+0

J'ai eu un problème similaire aggravé parce que nous voulions utiliser MixIns pour les annotations de Jackson plutôt que de les mettre sur les classes réelles. Nous avons résolu notre problème en créant notre propre annotation '@ JsonMixin', puis en recherchant tous les objets annotés au moment de l'exécution.Vous pouvez faire quelque chose de similaire pour configurer dynamiquement votre 'ObjectMapper' et n'en conserver qu'un seul. – Baldy

Questions connexes