¿Cómo serializar Joda DateTime con el procesador Jackson JSON?

118

¿Cómo hago para que Jackson serialice mi objeto Joda DateTime de acuerdo con un patrón simple (como "dd-MM-aaaa")?

He intentado:

@JsonSerialize(using=DateTimeSerializer.class)
private final DateTime date;

También he probado:

ObjectMapper mapper = new ObjectMapper()
    .getSerializationConfig()
    .setDateFormat(df);

¡Gracias!

Haywood Jablomey
fuente
Ambos de los anteriores también deberían funcionar (@JsonSerialize debería implicar que el campo se va a serializar; y el formato de fecha también debería aplicarse idealmente a Joda), por lo que es posible que desee presentar un error de Jira en jira.codehaus.org/browse/JACKSON .
StaxMan
Me doy cuenta de que esta pregunta es de hace un tiempo, pero para referencia futura, objectMapper.getSerializationConfig (). SetDateFormat (df) ahora está en desuso. Ahora se sugiere objectMapper.setDateFormat (df).
Patrick
Véase también stackoverflow.com/questions/27952472/…
Raedwald

Respuestas:

146

Esto se ha vuelto muy fácil con Jackson 2.0 y el módulo Joda.

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JodaModule());

Dependencia de Maven:

<dependency>
  <groupId>com.fasterxml.jackson.datatype</groupId>
  <artifactId>jackson-datatype-joda</artifactId>
  <version>2.1.1</version>
</dependency>  

Código y documentación: https://github.com/FasterXML/jackson-datatype-joda

Binarios: http://repo1.maven.org/maven2/com/fasterxml/jackson/datatype/jackson-datatype-joda/

Kimble
fuente
13
¿Podría ampliar esto por favor? ¿A dónde va este código y cómo se usa la instancia de ObjectMapper?
Paul
Debe hacer preguntas específicas sobre el uso de la API de Jackson y Maven. A dónde va el código depende de si usa algún marco / cómo inicia su aplicación.
Kimble
4
cuando agrego que obtengo el error de compilación "tipos incompatibles: JodaModule no se puede convertir en Módulo": el método espera un org.codehaus.jackson.map.Module pero JodaModule no tiene esto en su jerarquía, entonces, ¿cómo podría funcionar?
Martin Charlesworth
4
Prometedor pero actualmente casi inútil, ya que no podemos especificar el formato de fecha para analizar :(
Vincent Mimoun-Prat
10
Debe deshabilitar la serialización como marcas de tiempo objectMapper.disable (SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
MarekM
74

En el objeto que estás mapeando:

@JsonSerialize(using = CustomDateSerializer.class)
public DateTime getDate() { ... }

En CustomDateSerializer:

public class CustomDateSerializer extends JsonSerializer<DateTime> {

    private static DateTimeFormatter formatter = 
        DateTimeFormat.forPattern("dd-MM-yyyy");

    @Override
    public void serialize(DateTime value, JsonGenerator gen, 
                          SerializerProvider arg2)
        throws IOException, JsonProcessingException {

        gen.writeString(formatter.print(value));
    }
}
Rusty Kuntz
fuente
4
¿Cómo registrar CustomDateSerializer en Spring MVC 3?
digz6666
1
No sé sobre Spring MVC3, pero puede agregar un serializador personalizado al objeto mapeador de Jackson. Vea esta entrada en Jackson Necesita crear un módulo simple para ese SimpleModule isoDateTimeModule = new SimpleModule ("ISODateTimeModule", nueva versión (1, 0, 0, nulo)); isoDateTimeModule.addSerializer (nuevo JsonISODateTimeFormatSerializer ()); mapper.registerModule (isoDateTimeModule);
Guillaume Belrose
1
@ digz6666 vea mi respuesta aquí para Spring MVC 3: stackoverflow.com/questions/7854030/…
Aram Kocharyan
Nota: asegúrese de utilizar las clases anteriores de en org.codehaus.jacksonlugar de com.fasterxml.jackson.core. Usar el segundo paquete no funcionó para mí. De hecho, mi aplicación de Jersey ni siquiera respetó la @JsonSerializedanotación.
Kevin Meredith
La respuesta funcionó para mí. Pero requeriría esta anotación en todos los campos. ¿Existe alguna configuración global para esto que funcione con Jackson?
Taher
26

Como ha dicho @Kimble, con Jackson 2, usar el formato predeterminado es muy fácil; simplemente regístrese JodaModuleen su ObjectMapper.

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JodaModule());

Para la serialización / deserialización personalizada de DateTime, debe implementar su propia StdScalarSerializery StdScalarDeserializer; es bastante complicado, pero de todos modos.

Por ejemplo, aquí hay un DateTimeserializador que usa ISODateFormatcon la zona horaria UTC:

public class DateTimeSerializer extends StdScalarSerializer<DateTime> {

    public DateTimeSerializer() {
        super(DateTime.class);
    }

    @Override
    public void serialize(DateTime dateTime,
                          JsonGenerator jsonGenerator,
                          SerializerProvider provider) throws IOException, JsonGenerationException {
        String dateTimeAsString = ISODateTimeFormat.withZoneUTC().print(dateTime);
        jsonGenerator.writeString(dateTimeAsString);
    }
}

Y el deserializador correspondiente:

public class DateTimeDesrializer extends StdScalarDeserializer<DateTime> {

    public DateTimeDesrializer() {
        super(DateTime.class);
    }

    @Override
    public DateTime deserialize(JsonParser jsonParser,
                                DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
        try {
            JsonToken currentToken = jsonParser.getCurrentToken();
            if (currentToken == JsonToken.VALUE_STRING) {
                String dateTimeAsString = jsonParser.getText().trim();
                return ISODateTimeFormat.withZoneUTC().parseDateTime(dateTimeAsString);
            }
        } finally {
            throw deserializationContext.mappingException(getValueClass());
        }
    }

Luego, une estos con un módulo:

public class DateTimeModule extends SimpleModule {

    public DateTimeModule() {
        super();
        addSerializer(DateTime.class, new DateTimeSerializer());
        addDeserializer(DateTime.class, new DateTimeDeserializer());
    }
}

Luego registre el módulo en su ObjectMapper:

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new DateTimeModule());
oggmonster
fuente
el objeto DatatIme deserilado está en la zona horaria UTC o en la zona horaria de los navegadores, por favor
ayúdenme
¿Esto registra el mapeador (y los módulos) globalmente?
raffian
16

La solucion facil

Me he encontrado con un problema similar y mi solución es mucho más clara que la anterior.

Simplemente usé el patrón en la @JsonFormat anotación.

Básicamente, mi clase tiene un DateTimecampo, así que puse una anotación alrededor del captador:

@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
public DateTime getDate() {
    return date;
}

Serializo la clase con ObjectMapper

    ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(new JodaModule());
    mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    ObjectWriter ow = mapper.writer();
    try {
        String logStr = ow.writeValueAsString(log);
        outLogger.info(logStr);
    } catch (IOException e) {
        logger.warn("JSON mapping exception", e);
    }

Usamos Jackson 2.5.4

Atais
fuente
15

https://stackoverflow.com/a/10835114/1113510

Aunque puede poner una anotación para cada campo de fecha, es mejor hacer una configuración global para su mapeador de objetos. Si usa jackson, puede configurar su resorte de la siguiente manera:

<bean id="jacksonObjectMapper" class="com.company.CustomObjectMapper" />

<bean id="jacksonSerializationConfig" class="org.codehaus.jackson.map.SerializationConfig"
    factory-bean="jacksonObjectMapper" factory-method="getSerializationConfig" >
</bean>

Para CustomObjectMapper:

public class CustomObjectMapper extends ObjectMapper {

    public CustomObjectMapper() {
        super();
        configure(Feature.WRITE_DATES_AS_TIMESTAMPS, false);
        setDateFormat(new SimpleDateFormat("EEE MMM dd yyyy HH:mm:ss 'GMT'ZZZ (z)"));
    }
}

Por supuesto, SimpleDateFormat puede usar cualquier formato que necesite.

Moesio
fuente
4
setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssz"));me hizo el truco, gracias
Konsumierer
8

Mientras tanto, Jackson registra el módulo Joda automáticamente cuando JodaModule está en classpath. Acabo de agregar jackson-datatype-joda a Maven y funcionó al instante.

<dependency>
  <groupId>com.fasterxml.jackson.datatype</groupId>
  <artifactId>jackson-datatype-joda</artifactId>
  <version>2.8.7</version>
</dependency>

Salida JSON:

{"created" : "2017-03-28T05:59:27.258Z"}
Stephan
fuente
6

Para aquellos con Spring Boot, debe agregar el módulo a su contexto y se agregará a su configuración de esta manera.

@Bean
public Module jodaTimeModule() {
    return new JodaModule();
}

Y si desea utilizar el nuevo módulo de tiempo java8 jsr-310.

@Bean
public Module jodaTimeModule() {
    return new JavaTimeModule();
}
jgeerts
fuente
3

Parece que para Jackson 1.9.12 no existe tal posibilidad de forma predeterminada, debido a:

public final static class DateTimeSerializer
    extends JodaSerializer<DateTime>
{
    public DateTimeSerializer() { super(DateTime.class); }

    @Override
    public void serialize(DateTime value, JsonGenerator jgen, SerializerProvider provider)
        throws IOException, JsonGenerationException
    {
        if (provider.isEnabled(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS)) {
            jgen.writeNumber(value.getMillis());
        } else {
            jgen.writeString(value.toString());
        }
    }

    @Override
    public JsonNode getSchema(SerializerProvider provider, java.lang.reflect.Type typeHint)
    {
        return createSchemaNode(provider.isEnabled(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS)
                ? "number" : "string", true);
    }
}

Esta clase serializa datos utilizando el método toString () de Joda DateTime.

El enfoque propuesto por Rusty Kuntz funciona perfecto para mi caso.

ydrozhdzhal
fuente
Aún puede registrar un serializador y deserializador personalizados, y estos tendrán prioridad sobre la implementación predeterminada.
StaxMan
2

Estoy usando Java 8 y esto funcionó para mí.

Agregue la dependencia en pom.xml

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.4.0</version>
</dependency>

y agregue JodaModule en su ObjectMapper

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JodaModule());
Luciana Campello
fuente