2014-05-12 17 views
9

È possibile impostare Jersey utilizzando Jackson per serializzazione/deserializzazione utilizzando più configurato ObjectMappers?Utilizzo di Jackson in Jersey con più ObjectMappers configurati

Quello che mi piacerebbe essere in grado di fare è registrare un "default" Jackson ObjectMapper e quindi avere la capacità di registrare un'altra caratteristica che fornisce un ObjectMapper con qualche configurazione specializzato che, in determinate circostanze si "override" il "default" ObjectMapper.

Ad esempio, questo ContextResolver sarebbe per il mapper "default":

@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; 
    } 
} 

E questo ContextResolver sarebbe quello di ignorare il mapper "default":

@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; 
    } 
} 

vedo in JacksonJsonProvider codice che Jackson supporta l'iniezione/risoluzione del provider ObjectMapper. Tuttavia, in pratica, quello che vedo è che l'ordine dei fornitori sembra casuale (suppongo che non lo sia, ma non riesco a capire come controllare l'ordine). A volte il "override" viene prima del "default" e tutto funziona, ma all'avvio successivo del server l'ordine cambia.

ho cercato di farlo funzionare in diversi modi tra cui:

  • Registrazione dei ContextResolver<ObjectMapper> implementazioni manualmente (in ordini differenti)
  • Registrazione dei ContextResolver<ObjectMapper> implementazioni tramite @Provider annotazioni
  • Specifica un priorità al momento della registrazione

Sto usando il seguente:

  • Jersey 2,8
  • Jackson 2.3.3

Forse sto prendendo un approccio completamente errata?
Esiste un modo migliore per ottenere ciò che sto cercando di fare? Forse dovrei semplicemente definire due applicazioni JAX-RS separate e avere una singola configurazione ObjectMapper per ciascuna?

+0

Quello che ho trovato è una volta che hai 'ObjectMapper defaultMapper = return new ObjectMapper()', non è la stessa istanza predefinita di 'JacksonJsonProvider'. Atleast non per 'JacksonJaxbJsonProvider'. – ulab

risposta

2

È possibile configurare l'ordine dei provider, ma sarebbe in realtà essere meglio usare un fornitore in questa situazione:

@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

Grazie. Questo è quello che ho fatto inizialmente per farlo funzionare, tuttavia, speravo di mantenere le cose separate e agnostiche di diversi moduli/aree dell'applicazione e di evitare un potenziale "mega provider" che potrebbe derivare da diversi mapper necessari. Puoi approfondire perché _ "sarebbe meglio usare un fornitore in questa situazione" _? –

+0

Ho avuto un problema simile peggiorato perché volevamo usare MixIns per le annotazioni di Jackson piuttosto che metterle sulle classi reali. Abbiamo risolto il nostro problema creando la nostra annotazione '@ JsonMixin' e quindi eseguendo la scansione di tutti gli oggetti annotati in fase di esecuzione.Potresti fare qualcosa di simile per configurare il tuo 'ObjectMapper' in modo dinamico e mantenerne solo uno. – Baldy

Problemi correlati