springcloud採坑-jason序列化におけるDateオブジェクト

12674 ワード

要約:
    1.出現するシーン。
    2.エラー内容とコード追跡。
    3.原因
    4.3つの解決策。
1.    出現するシーン:
  •     サービス側はspringcloudインターフェースを提供します。
  •     クライアントは、feignを介してインターフェースを呼び出し、リターン値は、リストのCompanyDTであり、DTXにはDateオブジェクトがある。
  •     呼び出しエラー
  • 2.    エラーは以下の通りです
    Caused by: org.springframework.http.converter.HttpMessageNotReadableException: Could not read document: Can not deserialize value of type java.util.Date from String "2017-12-08 09:20:53": not a valid representation (error: Failed to parse Date value '2017-12-08 09:20:53': Can not parse date "2017-12-08 09:20:53Z": while it seems to fit format 'yyyy-MM-dd'T'HH:mm:ss.SSS'Z'', parsing fails (leniency? null))
     at [Source: java.io.PushbackInputStream@2041c1b0; line: 1, column: 554] (through reference chain: com.qts.base.result.ResponseData["data"]->java.util.ArrayList[0]->com.qts.company.api.dto.CompanyDTO["applyRefundTime"]); nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Can not deserialize value of type java.util.Date from String "2017-12-08 09:20:53": not a valid representation (error: Failed to parse Date value '2017-12-08 09:20:53': Can not parse date "2017-12-08 09:20:53Z": while it seems to fit format 'yyyy-MM-dd'T'HH:mm:ss.SSS'Z'', parsing fails (leniency? null))
     at [Source: java.io.PushbackInputStream@2041c1b0; line: 1, column: 554] (through reference chain: com.qts.base.result.ResponseData["data"]->java.util.ArrayList[0]->com.qts.company.api.dto.CompanyDTO["applyRefundTime"])
    	at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:228)
    	at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:213)
    	at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:95)
    	at org.springframework.cloud.netflix.feign.support.SpringDecoder.decode(SpringDecoder.java:59)
    	at org.springframework.cloud.netflix.feign.support.ResponseEntityDecoder.decode(ResponseEntityDecoder.java:47)
    	at feign.SynchronousMethodHandler.decode(SynchronousMethodHandler.java:165)
    	... 75 more
    Caused by: com.fasterxml.jackson.databind.exc.InvalidFormatException: Can not deserialize value of type java.util.Date from String "2017-12-08 09:20:53": not a valid representation (error: Failed to parse Date value '2017-12-08 09:20:53': Can not parse date "2017-12-08 09:20:53Z": while it seems to fit format 'yyyy-MM-dd'T'HH:mm:ss.SSS'Z'', parsing fails (leniency? null))
     at [Source: java.io.PushbackInputStream@2041c1b0; line: 1, column: 554] (through reference chain: com.qts.base.result.ResponseData["data"]->java.util.ArrayList[0]->com.qts.company.api.dto.CompanyDTO["applyRefundTime"])
    	at com.fasterxml.jackson.databind.exc.InvalidFormatException.from(InvalidFormatException.java:74)
    	at com.fasterxml.jackson.databind.DeserializationContext.weirdStringException(DeserializationContext.java:1410)
    	at com.fasterxml.jackson.databind.DeserializationContext.handleWeirdStringValue(DeserializationContext.java:926)
    	at com.fasterxml.jackson.databind.deser.std.StdDeserializer._parseDate(StdDeserializer.java:822)
    	at com.fasterxml.jackson.databind.deser.std.StdDeserializer._parseDate(StdDeserializer.java:791)
    	at com.fasterxml.jackson.databind.deser.std.DateDeserializers$DateBasedDeserializer._parseDate(DateDeserializers.java:172)
    	at com.fasterxml.jackson.databind.deser.std.DateDeserializers$DateDeserializer.deserialize(DateDeserializers.java:259)
    	at com.fasterxml.jackson.databind.deser.std.DateDeserializers$DateDeserializer.deserialize(DateDeserializers.java:242)
    	at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:499)
    	at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:101)
    	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:357)
    	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:148)
    	at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:277)
    	at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:249)
    	at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:26)
    	at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:499)
    	at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:101)
    	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:357)
    	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:148)
    	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3789)
    	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2913)
    	at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:225)
    	... 80 more
  • はAbstractJackson 2 HttpMessage Coverter.java:225のreadJava Type方法から始まります。
  • DateDeserializers.javaのdeserializeには、コアロジックがあります。customFormat.parse。問題が来ましたcustomFormatはどこから来ましたか?
  • protected Date _parseDate(JsonParser p, DeserializationContext ctxt) throws IOException {
                if (this._customFormat != null) {
                    JsonToken t = p.getCurrentToken();
                    if (t == JsonToken.VALUE_STRING) {
                        String str = p.getText().trim();
                        if (str.length() == 0) {
                            return (Date)this.getEmptyValue(ctxt);
                        }
    
                        DateFormat var5 = this._customFormat;
                        synchronized(this._customFormat) {
                            Date var10000;
                            try {
                                var10000 = this._customFormat.parse(str);//  
                            } catch (ParseException var8) {
                                return (Date)ctxt.handleWeirdStringValue(this.handledType(), str, "expected format \"%s\"", new Object[]{this._formatString});
                            }
    
                            return var10000;
                        }
                    }
                }
    
                return super._parseDate(p, ctxt);
            }
  •     DateBasedDeserializerの中の_customFormatは、ctxt.get Config()で取得し、Deserialization Contectは設定の文脈です。
  • protected abstract static class DateBasedDeserializer extends StdScalarDeserializer implements ContextualDeserializer {
            protected final DateFormat _customFormat;
            protected final String _formatString;
    
            protected abstract DateDeserializers.DateBasedDeserializer withDateFormat(DateFormat var1, String var2);
    
            public JsonDeserializer> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException {
                if (property != null) {
                    Value format = this.findFormatOverrides(ctxt, property, this.handledType());
                    if (format != null) {
                        TimeZone tz = format.getTimeZone();
                        Locale loc;
                        if (format.hasPattern()) {
                            String pattern = format.getPattern();
                            loc = format.hasLocale() ? format.getLocale() : ctxt.getLocale();
                            SimpleDateFormat df = new SimpleDateFormat(pattern, loc);
                            if (tz == null) {
                                tz = ctxt.getTimeZone();
                            }
    
                            df.setTimeZone(tz);
                            return this.withDateFormat(df, pattern);
                        }
    
                        if (tz != null) {
                            DateFormat df = ctxt.getConfig().getDateFormat();//  
                            Object df;
                            if (df.getClass() == StdDateFormat.class) {
                                loc = format.hasLocale() ? format.getLocale() : ctxt.getLocale();
                                StdDateFormat std = (StdDateFormat)df;
                                std = std.withTimeZone(tz);
                                std = std.withLocale(loc);
                                df = std;
                            } else {
                                df = (DateFormat)df.clone();
                                ((DateFormat)df).setTimeZone(tz);
                            }
    
                            return this.withDateFormat((DateFormat)df, this._formatString);
                        }
                    }
                }
    
                return this;
            }
        }
  •     私達はget DateFormatの具体的な実現を見て、最後にMapperConfigの中のBaseSeSettingsを通じて獲得します。
  •     public final DateFormat getDateFormat() {
            return this._base.getDateFormat();
        }
    3.具体的な理由はJasonがいくつかのフォーマットをサポートしているだけです。
    "yyyy-MM-dd'T'HH:mm:ss.SSSZ"  
     "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"  
     "EEE, dd MMM yyyy HH:mm:ss zzz"  
     "yyyy-MM-dd"  
    4.解決策:
        デフォルトのプログレッシブがこのようなフォーマットをサポートしていないなら、手動で設定してください。
        4.1クライアントのDateFormatを設定します。
    public class MyDateFormat extends DateFormat {
    
        private DateFormat dateFormat;
    
        private SimpleDateFormat format1 = new SimpleDateFormat("yyy-MM-dd HH:mm:ss");
    
        public MyDateFormat(DateFormat dateFormat) {
            this.dateFormat = dateFormat;
        }
    
        @Override
        public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) {
            return dateFormat.format(date, toAppendTo, fieldPosition);
        }
    
        @Override
        public Date parse(String source, ParsePosition pos) {
    
            Date date = null;
    
            try {
    
                date = format1.parse(source, pos);
            } catch (Exception e) {
    
                date = dateFormat.parse(source, pos);
            }
    
            return date;
        }
    
        //           
        @Override
        public Date parse(String source) throws ParseException {
    
            Date date = null;
    
            try {
    
                //        
                date = format1.parse(source);
            } catch (Exception e) {
    
                //   ,         
                date = dateFormat.parse(source);
            }
    
            return date;
        }
    
        //     clone        clone   jackson     
        @Override
        public Object clone() {
            Object format = dateFormat.clone();
            return new MyDateFormat((DateFormat) format);
        }
    }
    
        そして起動時に加入します。
    @Configuration
    public class WebConfig {
    
    	@Autowired
    	private Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder;
    	
    	@Bean
    	public MappingJackson2HttpMessageConverter MappingJsonpHttpMessageConverter() {
    
    		ObjectMapper mapper = jackson2ObjectMapperBuilder.build();
    
    		// ObjectMapper         ,                
    		//      setDateFormat                  
    		DateFormat dateFormat = mapper.getDateFormat();
    		mapper.setDateFormat(new MyDateFormat(dateFormat));
    
    		MappingJackson2HttpMessageConverter mappingJsonpHttpMessageConverter = new MappingJackson2HttpMessageConverter(
    				mapper);
    		return mappingJsonpHttpMessageConverter;
    	}
    }
    4.2ソリューション2
        コードで構成できます。
    #spring.mvc.date-format=yyyy-MM-dd HH:mm:ss
    #spring.jackson.time-zone=GMT+8
    #spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
    #spring.jackson.deserialization.accept_empty_string_as_null_object=true
    4.3解決策3
        フィールドのsetterに注釈を加えるのは、この方法が面倒くさいです。
    public class DateJsonDeserializer extends JsonDeserializer {
    
        /**
         * @see JsonDeserializer#deserialize(JsonParser,
         *      DeserializationContext)
         */
        @Override
        public Date deserialize(JsonParser parser, DeserializationContext context)
                throws IOException, JsonProcessingException {
            try {
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                return sdf.parse(parser.getValueAsString());
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    
    }
    DTMのフィールドに注釈を追加します。
        @JsonDeserialize(using = DateJsonDeserializer.class)
        private Date applyRefundTime;
    参考記事:
    https://blog.csdn.net/qq906627950/article/details/79503801
    http://wujiu.iteye.com/blog/2244537