Formato della data Mapping to JSON Jackson

Ho un formato di data proveniente da API come questo:

"start_time": "2015-10-1 3:00 PM GMT+1:00" 

Che è YYYY-DD-MM HH: MM am / pm GMT timestamp. Sto mappando questo valore ad una variabile Date in POJO. Ovviamente, il suo errore di conversione mostra.

Mi piacerebbe sapere 2 cose:

  1. Qual è la formattazione che devo usare per effettuare la conversione con Jackson? Data è un buon tipo di campo per questo?
  2. In generale, c’è un modo per elaborare le variabili prima che vengano mappate ai membri Object di Jackson? Qualcosa come, cambiando il formato, i calcoli, ecc.

    Qual è la formattazione che devo usare per effettuare la conversione con Jackson? Data è un buon tipo di campo per questo?

    Date è un tipo di campo fine per questo. È ansible rendere l’analisi JSON abbastanza facile utilizzando ObjectMapper.setDateFormat :

     DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm az"); myObjectMapper.setDateFormat(df); 

    In generale, c’è un modo per elaborare le variabili prima che vengano mappate ai membri Object di Jackson? Qualcosa come, cambiando il formato, i calcoli, ecc.

    Sì. Hai alcune opzioni, inclusa l’implementazione di un JsonDeserializer personalizzato, ad esempio l’estensione di JsonDeserializer . Questo è un buon inizio.

    Da quando Jackson v2.0, puoi usare l’annotazione @JsonFormat direttamente sui membri Object;

     @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm az") private Date date; 

    Ovviamente esiste un modo automatico chiamato serializzazione e deserializzazione e puoi definirlo con annotazioni specifiche ( @JsonSerialize , @JsonDeserialize ) come menzionato anche da pb2q.

    Puoi utilizzare sia java.util.Date sia java.util.Calendar … e probabilmente anche JodaTime.

    Le annotazioni @JsonFormat non hanno funzionato per me come volevo (ha regolato il fuso orario su un valore diverso) durante la deserializzazione (la serializzazione ha funzionato perfettamente):

     @JsonFormat(locale = "hu", shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm", timezone = "CET") @JsonFormat(locale = "hu", shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm", timezone = "Europe/Budapest") 

    È necessario utilizzare il serializzatore personalizzato e il deserializzatore personalizzato invece dell’annotazione @JsonFormat se si desidera un risultato previsto. Ho trovato tutorial e soluzioni davvero validi qui http://www.baeldung.com/jackson-serialize-dates

    Esistono degli esempi per i campi Data , ma io avevo bisogno dei campi Calendario , ecco la mia implementazione :

    La class serializzatore :

     public class CustomCalendarSerializer extends JsonSerializer { public static final SimpleDateFormat FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm"); public static final Locale LOCALE_HUNGARIAN = new Locale("hu", "HU"); public static final TimeZone LOCAL_TIME_ZONE = TimeZone.getTimeZone("Europe/Budapest"); @Override public void serialize(Calendar value, JsonGenerator gen, SerializerProvider arg2) throws IOException, JsonProcessingException { if (value == null) { gen.writeNull(); } else { gen.writeString(FORMATTER.format(value.getTime())); } } } 

    La class di deserializzatore :

     public class CustomCalendarDeserializer extends JsonDeserializer { @Override public Calendar deserialize(JsonParser jsonparser, DeserializationContext context) throws IOException, JsonProcessingException { String dateAsString = jsonparser.getText(); try { Date date = CustomCalendarSerializer.FORMATTER.parse(dateAsString); Calendar calendar = Calendar.getInstance( CustomCalendarSerializer.LOCAL_TIME_ZONE, CustomCalendarSerializer.LOCALE_HUNGARIAN ); calendar.setTime(date); return calendar; } catch (ParseException e) { throw new RuntimeException(e); } } } 

    e l’uso delle classi precedenti:

     public class CalendarEntry { @JsonSerialize(using = CustomCalendarSerializer.class) @JsonDeserialize(using = CustomCalendarDeserializer.class) private Calendar calendar; // ... additional things ... } 

    Usando questa implementazione l’esecuzione del processo di serializzazione e deserializzazione risulta consecutivamente al valore di origine.

    Solo usando l’annotazione @JsonFormat la deserializzazione dà risultati diversi, penso che a causa della configurazione predefinita del fuso orario interno della libreria, ciò che non è ansible modificare con i parametri di annotazione (era la mia esperienza con la versione 2.5.3 e 2.6.3 della libreria di Jackson).

    Basandomi sulla risposta molto utile di @ miklov-kriven, spero che questi due ulteriori punti di considerazione si dimostrino utili a qualcuno:

    (1) Trovo una buona idea includere serializzatore e de-serializer come classi interne statiche nella stessa class. NB, utilizzando ThreadLocal per la sicurezza del thread di SimpleDateFormat.

     public class DateConverter { private static final ThreadLocal sdf = ThreadLocal.withInitial( () -> {return new SimpleDateFormat("yyyy-MM-dd HH:mm az");}); public static class Serialize extends JsonSerializer { @Override public void serialize(Date value, JsonGenerator jgen SerializerProvider provider) throws Exception { if (value == null) { jgen.writeNull(); } else { jgen.writeString(sdf.get().format(value)); } } } public static class Deserialize extends JsonDeserializer { @Overrride public Date deserialize(JsonParser jp, DeserializationContext ctxt) throws Exception { String dateAsString = jp.getText(); try { if (Strings.isNullOrEmpty(dateAsString)) { return null; } else { return new Date(sdf.get().parse(dateAsString).getTime()); } } catch (ParseException pe) { throw new RuntimeException(pe); } } } } 

    (2) In alternativa all’utilizzo delle annotazioni @JsonSerialize e @JsonDeserialize su ogni singolo membro della class, potresti anche considerare di ignorare la serializzazione predefinita di Jackson applicando la serializzazione personalizzata a livello di applicazione, ovvero tutti i membri della class di tipo Date verranno serializzati da Jackson utilizzando questa serializzazione personalizzata senza annotazioni esplicite su ciascun campo. Se stai usando Spring Boot, ad esempio, un modo per farlo sarebbe il seguente:

     @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } @Bean public Module customModule() { SimpleModule module = new SimpleModule(); module.addSerializer(Date.class, new DateConverter.Serialize()); module.addDeserializer(Date.class, new Dateconverter.Deserialize()); return module; } } 

    Se qualcuno ha problemi con l’utilizzo di un dataformat personalizzato per java.sql.Date, questa è la soluzione più semplice:

     ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); module.addSerializer(java.sql.Date.class, new DateSerializer()); mapper.registerModule(module); 

    (Questa risposta SO mi ha salvato un sacco di problemi: https://stackoverflow.com/a/35212795/3149048 )

    Jackson usa SqlDateSerializer per impostazione predefinita per java.sql.Date, ma al momento questo serializzatore non tiene conto del dataformat, vedere questo problema: https://github.com/FasterXML/jackson-databind/issues/1407 . La soluzione alternativa è registrare un serializzatore diverso per java.sql.Date come mostrato nell’esempio di codice.

    Un esempio completo per l’applicazione di avvio a molla con formato datetime RFC3339

     package bj.demo; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.ApplicationListener; import java.text.SimpleDateFormat; /** * Created by BaiJiFeiLong@gmail.com at 2018/5/4 10:22 */ @SpringBootApplication public class BarApp implements ApplicationListener { public static void main(String[] args) { SpringApplication.run(BarApp.class, args); } @Autowired private ObjectMapper objectMapper; @Override public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) { objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX")); } }