तिथि प्रारूप मैपिंग JSON जैक्सन के लिए


154

मेरे पास इस तरह एपीआई से आने वाला एक तारीख प्रारूप है:

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

जो YYYY-DD-MM HH है: MM am / pm GMT टाइमस्टैम्प। मैं इस मूल्य को POJO में दिनांक चर पर मैप कर रहा हूं। जाहिर है, इसकी रूपांतरण रूपांतरण त्रुटि।

मैं 2 बातें जानना चाहूंगा:

  1. जैक्सन के साथ रूपांतरण करने के लिए मुझे किस प्रारूपण का उपयोग करने की आवश्यकता है? क्या दिनांक इसके लिए एक अच्छा फ़ील्ड प्रकार है?
  2. सामान्य तौर पर, क्या जैक्सन द्वारा ऑब्जेक्ट सदस्यों को मैप किए जाने से पहले चर को संसाधित करने का एक तरीका है? जैसे कुछ, स्वरूप बदलना, गणना करना आदि।

यह वास्तव में अच्छा उदाहरण है, वर्ग के क्षेत्र में जगह का अनावरण
digz6666

अब उनके पास तारीख से निपटने के लिए एक विकी पेज है: wiki.fasterxml.com/JacksonFAQDateHandling
Sutra

इन दिनों आपको Dateकक्षा का उपयोग नहीं करना चाहिए । java.time, आधुनिक जावा तिथि और समय एपीआई, ने इसे लगभग 5 साल पहले बदल दिया है। इसे और फास्टरएक्सएमएल / जैकसन-मॉड्यूल-जावा 8 का उपयोग करें
ओले वीवी

जवाबों:


124

जैक्सन के साथ रूपांतरण करने के लिए मुझे किस प्रारूपण का उपयोग करने की आवश्यकता है? क्या दिनांक इसके लिए एक अच्छा फ़ील्ड प्रकार है?

Dateइसके लिए एक अच्छा क्षेत्र प्रकार है। आप JSON पार्स-सक्षम का उपयोग करके बहुत आसानी से कर सकते हैं ObjectMapper.setDateFormat:

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

सामान्य तौर पर, क्या जैक्सन द्वारा ऑब्जेक्ट सदस्यों को मैप किए जाने से पहले चर को संसाधित करने का एक तरीका है? जैसे कुछ, स्वरूप बदलना, गणना करना आदि।

हाँ। आपके पास कुछ विकल्प हैं, जिसमें एक कस्टम को लागू करना JsonDeserializer, जैसे कि विस्तार करना JsonDeserializer<Date>यह एक अच्छी शुरुआत है।


2
12 घंटे का प्रारूप बेहतर है यदि प्रारूप में AM / PM पदनाम भी शामिल है: DateFormat df = new SimpleDateFormat ("yyyy-MM-dd hh: mm a z");
जॉन स्कैटरगूड

इन सभी समाधानों की कोशिश की, लेकिन मेरे POJO के दिनांक चर को मैप कुंजी में स्टोर करने में सक्षम नहीं था, दिनांक के रूप में भी। मैं चाहता हूं कि यह मैप से एक BasicDbObject (MongoDB API) को इंस्टेंट कर दे, और परिणामस्वरूप MongoDB DB संग्रह में दिनांक (नहीं लंबे या स्ट्रिंग के रूप में) चर को संग्रहीत करें। क्या यह भी संभव है? धन्यवाद
RedEagle

1
क्या जावा 8 का उपयोग करना आसान है LocalDateTimeया ZonedDateTimeइसके बजाय Date? चूंकि Dateमूल रूप से पदावनत (या इसके कई तरीके) हैं, मैं उन विकल्पों का उपयोग करना चाहूंगा।
होउकॉस

SetSateFormat () के लिए javadocs कहता है कि यह कॉल ObjectMapper को थ्रेड सुरक्षित नहीं बनाता है। मैंने JsonSerializer और JsonDeserializer कक्षाएं बनाईं।
मिगेलमुनोज

जैसा कि प्रश्न java.util.Dateस्पष्ट रूप से उल्लेख नहीं करता है, मैं यह बताना चाहता हूं कि यह java.sql.Date.नीचे दिए गए मेरे जवाब के लिए भी काम नहीं करता है ।
पीतल बंदर

329

जैक्सन v2.0 के बाद से, आप ऑब्जेक्ट सदस्यों पर सीधे @JsonFormat एनोटेशन का उपयोग कर सकते हैं;

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

61
यदि आप @JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", timezone="GMT")
टाइमजोन

हाय, किस जार में वह एनोटेशन है। मैं जैकसन-मैपर-एसएल 1.9.10 संस्करण का उपयोग कर रहा हूं। मुझे नहीं मिल रहा है
krishna Ram

1
@ रामकी: जैकसन-एनोटेशन> = 2.0
ओलिवियर लेक्रिवेन

3
यह एनोटेशन केवल क्रमांकन चरण में सही काम करता है, लेकिन डीज़ेरिएलाइज़ेशन के दौरान समय-क्षेत्र और स्थानीय जानकारी का उपयोग नहीं किया जाता है। मैंने समय-सीमा = "सीईटी" और टाइमज़ोन "यूरोप / बुडापेस्ट" को लोकेल = "हू" के साथ आजमाया है, लेकिन कैलेंडर पर अजीब समय बातचीत का काम नहीं करता है और न ही इसका कारण बनता है। केवल आवश्यकतानुसार सीरियलाइज़ेशन विथ डिसिकायलाइज़ेशन ने मेरे लिए समयसीमा को संभालने का काम किया। यहाँ सही ट्यूटोरियल है कि आपको baeldung.com/jackson-serialize-dates
Miklos Krivan

1
यह एक अलग जार परियोजना है जिसे जैक्सन एनोटेशन कहा जाता है । नमूना पोम प्रविष्टि
बुब

52

निश्चित रूप से एक स्वचालित तरीका है जिसे क्रमबद्धता और डिसेरिएलाइज़ेशन कहा जाता है और आप इसे विशिष्ट एनोटेशन ( @JsonSerialize , @JsonDeserialize ) के साथ परिभाषित कर सकते हैं जैसा कि pb2q द्वारा भी उल्लेख किया गया है।

आप java.util.Date और java.util.Calendar ... और शायद JodaTime दोनों का उपयोग कर सकते हैं।

@JsonFormat एनोटेशन मेरे लिए काम नहीं के रूप में मैं चाहता था (यह है समय क्षेत्र समायोजित भिन्न मान पर) अक्रमांकन दौरान (क्रमांकन सही काम किया):

@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")

यदि आप अनुमानित परिणाम चाहते हैं तो आपको @JsonFormat एनोटेशन के बजाय कस्टम धारावाहिक और कस्टम deserializer का उपयोग करने की आवश्यकता है। मुझे असली अच्छा ट्यूटोरियल और समाधान यहां मिला है http://www.baeldung.com/jackson-serialize-dates

दिनांक फ़ील्ड के लिए उदाहरण हैं, लेकिन मुझे कैलेंडर फ़ील्ड के लिए आवश्यक है, इसलिए यहां मेरा कार्यान्वयन है :

Serializer वर्ग:

public class CustomCalendarSerializer extends JsonSerializer<Calendar> {

    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()));
        }
    }
}

Deserializer वर्ग:

public class CustomCalendarDeserializer extends JsonDeserializer<Calendar> {

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

और उपरोक्त वर्गों का उपयोग :

public class CalendarEntry {

    @JsonSerialize(using = CustomCalendarSerializer.class)
    @JsonDeserialize(using = CustomCalendarDeserializer.class)
    private Calendar calendar;

    // ... additional things ...
}

इस कार्यान्वयन का उपयोग क्रमबद्धता और डीरियलाइज़ेशन प्रक्रिया के निष्पादन के परिणामस्वरूप मूल मूल्य में लगातार होता है।

केवल @JsonFormat एनोटेशन का उपयोग करते हुए deserialization अलग-अलग परिणाम देता है जो मुझे लगता है कि लाइब्रेरी आंतरिक टाइमज़ोन डिफ़ॉल्ट सेटअप के कारण है जो आप एनोटेशन मापदंडों के साथ नहीं बदल सकते हैं (जो कि जैक्सन लाइब्रेरी के साथ मेरा अनुभव 2.5.3 और 2.6.3 संस्करण के साथ भी था)।


4
मैं कल अपने जवाब के लिए उतर गया हूं। मैंने इस विषय पर बहुत काम किया इसलिए मुझे समझ नहीं आया। क्या मुझे इससे सीखने के लिए कुछ प्रतिक्रिया मिली है? मैं डाउनवोट के मामले में कुछ नोटों की सराहना करूंगा। इस तरह हम एक-दूसरे से और सीख सकते हैं।
मिकलोस क्रिवन

महान जवाब, धन्यवाद यह वास्तव में मेरी मदद की! मामूली सुझाव - एक संलग्न माता-पिता वर्ग के भीतर CustomCalendarSerializer और CustomCalendarDeserializer को स्थिर वर्ग के रूप में रखने पर विचार करें। मुझे लगता है कि यह कोड को थोड़ा अच्छा बना देगा :)
स्टुअर्ट

@ स्टुअर्ट - आपको बस एक और जवाब के रूप में कोड के इस पुनर्गठन की पेशकश करनी चाहिए, या एक संपादन का प्रस्ताव करना चाहिए। मिक्लोस के पास ऐसा करने के लिए स्वतंत्र समय नहीं हो सकता है।

@MiklosKrivan मैंने आपको कई कारणों से नीचा दिखाया है। आपको पता होना चाहिए कि SimpleDateFormat थ्रेडसेफ़ नहीं है और स्पष्ट रूप से आपको दिनांक स्वरूपण के लिए वैकल्पिक पुस्तकालयों (जोडा, कॉमन्स-लैंग FastDateFormat आदि) का उपयोग करना चाहिए। दूसरा कारण टाइमजोन और यहां तक ​​कि लोकेल की स्थापना है। यह क्रमिक वातावरण में GMT का उपयोग करने के लिए कहीं अधिक बेहतर है और अपने ग्राहक को यह कहने की अनुमति दें कि यह किस समयक्षेत्र में है या एक अलग स्ट्रिंग के रूप में पसंदीदा tz संलग्न करें। अपना सर्वर GMT उर्फ ​​UTC पर होना चाहिए। जैक्सन के पास आईएसओ प्रारूप है।
एडम गेंट

1
आपकी प्रतिक्रिया के लिए Thx @AdamGent। मैं आपके सुझावों को समझता हूं और स्वीकार करता हूं। लेकिन इस विशेष मामले में मैं सिर्फ इस बात पर ध्यान केंद्रित करना चाहता था कि स्थानीय जानकारी के साथ JsonFormat एनोटेशन उस तरह से काम नहीं कर रहा है जैसा हम उम्मीद करेंगे। और कैसे हल किया जा सकता है।
मिकलोस क्रिवन

4

डेटाइम फॉर्मेट के साथ स्प्रिंग बूट एप्लिकेशन के लिए एक पूर्ण उदाहरण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<ApplicationReadyEvent> {

    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"));
    }
}

4

अपनी तिथि में T और Z जैसे अक्षर जोड़ने के लिए

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss'Z'")
private Date currentTime;

उत्पादन

{
    "currentTime": "2019-12-11T11:40:49Z"
}

3

मेरे लिए काम कर रहा है। SpringBoot।

 import com.alibaba.fastjson.annotation.JSONField;

 @JSONField(format = "yyyy-MM-dd HH:mm:ss")  
 private Date createTime;

उत्पादन:

{ 
   "createTime": "2019-06-14 13:07:21"
}

2

Miklov-kriven के बहुत उपयोगी उत्तर पर बिल्डिंग, मुझे उम्मीद है कि विचार के ये दो अतिरिक्त बिंदु किसी के लिए उपयोगी साबित होंगे:

(1) मुझे एक ही कक्षा में स्थिर आंतरिक कक्षाओं के रूप में धारावाहिक और डी-धारावाहिक को शामिल करना एक अच्छा विचार है। SimpleDateFormat के थ्रेड सुरक्षा के लिए थ्रेडलोक का उपयोग करते हुए एनबी।

public class DateConverter {

    private static final ThreadLocal<SimpleDateFormat> sdf = 
        ThreadLocal.<SimpleDateFormat>withInitial(
                () -> {return new SimpleDateFormat("yyyy-MM-dd HH:mm a z");});

    public static class Serialize extends JsonSerializer<Date> {
        @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<Date> {
        @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) प्रत्येक व्यक्तिगत वर्ग के सदस्य पर @JsonSerialize और @JsonDeserialize एनोटेशन का उपयोग करने के विकल्प के रूप में, आप एक आवेदन स्तर पर कस्टम क्रमांकन लागू करके जैक्सन के डिफ़ॉल्ट क्रमांकन को ओवरराइड करने पर भी विचार कर सकते हैं, यह है कि सभी प्रकार के क्लास के सदस्यों को जैक्सन द्वारा सीरियल किया जाएगा। प्रत्येक क्षेत्र पर स्पष्ट एनोटेशन के बिना इस कस्टम क्रमांकन का उपयोग करना। यदि आप उदाहरण के लिए स्प्रिंग बूट का उपयोग कर रहे हैं, तो यह इस प्रकार है:

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

मैंने आपको नीचा दिखाया (मुझे नीच से नफरत है लेकिन मैं नहीं चाहता कि लोग आपके उत्तर का उपयोग करें)। SimpleDateFormat थ्रेडसेफ़ नहीं है। यह 2016 है (आपने 2016 में उत्तर दिया)। आप SimpleDateFormat का उपयोग नहीं किया जाना चाहिए जब कई तेज और थ्रेडसेफ़ विकल्प हैं। यहाँ पर आप Q / A का जो प्रस्ताव दे रहे हैं उसका भी सटीक दुरुपयोग है: stackoverflow.com/questions/25680728/…
एडम Gent

2
@ बाहर का इशारा इस ओर इशारा करने के लिए। इस संदर्भ में, जैक्सन का उपयोग करके, ObjectMapper वर्ग थ्रेड सुरक्षित है, इसलिए यह कोई फर्क नहीं पड़ता। हालाँकि, मैं आपकी बात पर ध्यान देता हूं कि कोड की प्रतिलिपि बनाई जा सकती है और इसका उपयोग गैर-थ्रेड सुरक्षित संदर्भ में किया जा सकता है। इसलिए मैंने SimpleDateFormat थ्रेड को सुरक्षित बनाने के लिए अपना उत्तर संपादित किया है। मैं यह भी स्वीकार करता हूं कि विकल्प हैं, मुख्य रूप से जावा.टाइम पैकेज।
स्टुअर्ट

2

अगर किसी को java.sql.Date के लिए कस्टम डेटफॉर्मेट का उपयोग करने में समस्या है, तो यह सबसे सरल उपाय है:

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

(इस एसओ-जवाब ने मुझे बहुत परेशानी से बचाया: https://stackoverflow.com/a/35212795/3149048 )

जैक्सन डिफ़ॉल्ट रूप से java.sql.Date के लिए SqlDateSerializer का उपयोग करता है, लेकिन वर्तमान में, यह धारावाहिक तारीख को ध्यान में नहीं रखता है, इस मुद्दे को देखें: https://github.com/FasterXML/jackson -databind/issues/1407 । वर्कअराउंड java.sql.Date के लिए एक अलग धारावाहिक को पंजीकृत करना है जैसा कि कोड उदाहरण में दिखाया गया है।


1

मैं यह बताना चाहता हूं कि SimpleDateFormatअन्य उत्तर में वर्णित एक सेट करना केवल उसी के लिए काम करता है java.util.Dateजिसके लिए मैं मानता हूं कि प्रश्न में अभिप्रेत है। लेकिन java.sql.Dateफॉर्मेटर के लिए काम नहीं करता है। मेरे मामले में यह बहुत स्पष्ट नहीं था कि फॉर्मेटर क्यों काम नहीं करता था क्योंकि मॉडल में जो क्षेत्र क्रमबद्ध होना चाहिए था वह वास्तव में था, java.utl.Dateलेकिन वास्तविक वस्तु एक मधुमक्खी को समाप्त कर देती है java.sql.Date। ऐसा संभव है

public class java.sql extends java.util.Date

तो यह वास्तव में वैध है

java.util.Date date = new java.sql.Date(1542381115815L);

इसलिए यदि आप सोच रहे हैं कि आपका दिनांक फ़ील्ड सही रूप से स्वरूपित क्यों नहीं है, तो सुनिश्चित करें कि ऑब्जेक्ट वास्तव में है java.util.Date

यहां यह भी बताया गया है कि हैंडलिंग क्यों java.sql.Dateनहीं जोड़ी जाएगी।

यह तब परिवर्तन को तोड़ने वाला होगा, और मुझे नहीं लगता कि इस पर वार किया गया है। यदि हम खरोंच से शुरू कर रहे थे तो मैं बदलाव से सहमत होगा, लेकिन जैसा कि चीजें इतनी नहीं हैं।


1
दो अलग-अलग Dateवर्गों के इस बुरे डिजाइन के एक परिणाम को इंगित करने के लिए धन्यवाद । इन दिनों किसी को भी उनमें से कोई भी नहीं होना चाहिए, न ही SimpleDateFormatjava.time, आधुनिक जावा तिथि और समय एपीआई, ने उन्हें लगभग 5 साल पहले बदल दिया है। इसे और फास्टरएक्सएमएल / जैकसन-मॉड्यूल-जावा 8 का उपयोग करें
ओले वीवी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.