1 package com.fasterxml.jackson.databind.deser.std; 2 3 import java.io.IOException; 4 import java.lang.reflect.Constructor; 5 import java.sql.Timestamp; 6 import java.text.*; 7 import java.util.*; 8 9 import com.fasterxml.jackson.annotation.JsonFormat; 10 11 import com.fasterxml.jackson.core.JsonParser; 12 import com.fasterxml.jackson.core.JsonToken; 13 14 import com.fasterxml.jackson.databind.*; 15 import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; 16 import com.fasterxml.jackson.databind.cfg.CoercionAction; 17 import com.fasterxml.jackson.databind.deser.ContextualDeserializer; 18 import com.fasterxml.jackson.databind.type.LogicalType; 19 import com.fasterxml.jackson.databind.util.ClassUtil; 20 import com.fasterxml.jackson.databind.util.StdDateFormat; 21 22 /** 23 * Container class for core JDK date/time type deserializers. 24 */ 25 @SuppressWarnings("serial") 26 public class DateDeserializers 27 { 28 private final static HashSet<String> _classNames = new HashSet<String>(); 29 static { 30 Class<?>[] numberTypes = new Class<?>[] { 31 Calendar.class, 32 GregorianCalendar.class, 33 java.sql.Date.class, 34 java.util.Date.class, 35 Timestamp.class, 36 }; 37 for (Class<?> cls : numberTypes) { cls.getName()38 _classNames.add(cls.getName()); 39 } 40 } 41 find(Class<?> rawType, String clsName)42 public static JsonDeserializer<?> find(Class<?> rawType, String clsName) 43 { 44 if (_classNames.contains(clsName)) { 45 // Start with the most common type 46 if (rawType == Calendar.class) { 47 return new CalendarDeserializer(); 48 } 49 if (rawType == java.util.Date.class) { 50 return DateDeserializer.instance; 51 } 52 if (rawType == java.sql.Date.class) { 53 return new SqlDateDeserializer(); 54 } 55 if (rawType == Timestamp.class) { 56 return new TimestampDeserializer(); 57 } 58 if (rawType == GregorianCalendar.class) { 59 return new CalendarDeserializer(GregorianCalendar.class); 60 } 61 } 62 return null; 63 } 64 65 // @since 2.11 hasDeserializerFor(Class<?> rawType)66 public static boolean hasDeserializerFor(Class<?> rawType) { 67 return _classNames.contains(rawType.getName()); 68 } 69 70 /* 71 /********************************************************** 72 /* Intermediate class for Date-based ones 73 /********************************************************** 74 */ 75 76 protected abstract static class DateBasedDeserializer<T> 77 extends StdScalarDeserializer<T> 78 implements ContextualDeserializer 79 { 80 /** 81 * Specific format to use, if non-null; if null will 82 * just use default format. 83 */ 84 protected final DateFormat _customFormat; 85 86 /** 87 * Let's also keep format String for reference, to use for error messages 88 */ 89 protected final String _formatString; 90 DateBasedDeserializer(Class<?> clz)91 protected DateBasedDeserializer(Class<?> clz) { 92 super(clz); 93 _customFormat = null; 94 _formatString = null; 95 } 96 DateBasedDeserializer(DateBasedDeserializer<T> base, DateFormat format, String formatStr)97 protected DateBasedDeserializer(DateBasedDeserializer<T> base, 98 DateFormat format, String formatStr) { 99 super(base._valueClass); 100 _customFormat = format; 101 _formatString = formatStr; 102 } 103 withDateFormat(DateFormat df, String formatStr)104 protected abstract DateBasedDeserializer<T> withDateFormat(DateFormat df, String formatStr); 105 106 @Override // since 2.12 logicalType()107 public LogicalType logicalType() { 108 return LogicalType.DateTime; 109 } 110 111 @Override createContextual(DeserializationContext ctxt, BeanProperty property)112 public JsonDeserializer<?> createContextual(DeserializationContext ctxt, 113 BeanProperty property) 114 throws JsonMappingException 115 { 116 final JsonFormat.Value format = findFormatOverrides(ctxt, property, 117 handledType()); 118 119 if (format != null) { 120 TimeZone tz = format.getTimeZone(); 121 final Boolean lenient = format.getLenient(); 122 123 // First: fully custom pattern? 124 if (format.hasPattern()) { 125 final String pattern = format.getPattern(); 126 final Locale loc = format.hasLocale() ? format.getLocale() : ctxt.getLocale(); 127 SimpleDateFormat df = new SimpleDateFormat(pattern, loc); 128 if (tz == null) { 129 tz = ctxt.getTimeZone(); 130 } 131 df.setTimeZone(tz); 132 if (lenient != null) { 133 df.setLenient(lenient); 134 } 135 return withDateFormat(df, pattern); 136 } 137 // But if not, can still override timezone 138 if (tz != null) { 139 DateFormat df = ctxt.getConfig().getDateFormat(); 140 // one shortcut: with our custom format, can simplify handling a bit 141 if (df.getClass() == StdDateFormat.class) { 142 final Locale loc = format.hasLocale() ? format.getLocale() : ctxt.getLocale(); 143 StdDateFormat std = (StdDateFormat) df; 144 std = std.withTimeZone(tz); 145 std = std.withLocale(loc); 146 if (lenient != null) { 147 std = std.withLenient(lenient); 148 } 149 df = std; 150 } else { 151 // otherwise need to clone, re-set timezone: 152 df = (DateFormat) df.clone(); 153 df.setTimeZone(tz); 154 if (lenient != null) { 155 df.setLenient(lenient); 156 } 157 } 158 return withDateFormat(df, _formatString); 159 } 160 // or maybe even just leniency? 161 if (lenient != null) { 162 DateFormat df = ctxt.getConfig().getDateFormat(); 163 String pattern = _formatString; 164 // one shortcut: with our custom format, can simplify handling a bit 165 if (df.getClass() == StdDateFormat.class) { 166 StdDateFormat std = (StdDateFormat) df; 167 std = std.withLenient(lenient); 168 df = std; 169 pattern = std.toPattern(); 170 } else { 171 // otherwise need to clone, 172 df = (DateFormat) df.clone(); 173 df.setLenient(lenient); 174 if (df instanceof SimpleDateFormat) { 175 ((SimpleDateFormat) df).toPattern(); 176 } 177 } 178 if (pattern == null) { 179 pattern = "[unknown]"; 180 } 181 return withDateFormat(df, pattern); 182 } 183 } 184 return this; 185 } 186 187 @Override _parseDate(JsonParser p, DeserializationContext ctxt)188 protected java.util.Date _parseDate(JsonParser p, DeserializationContext ctxt) 189 throws IOException 190 { 191 if (_customFormat != null) { 192 if (p.hasToken(JsonToken.VALUE_STRING)) { 193 String str = p.getText().trim(); 194 if (str.length() == 0) { 195 final CoercionAction act = _checkFromStringCoercion(ctxt, str); 196 switch (act) { // note: Fail handled above 197 case AsEmpty: 198 return new java.util.Date(0L); 199 case AsNull: 200 case TryConvert: 201 default: 202 } 203 return null; 204 } 205 synchronized (_customFormat) { 206 try { 207 return _customFormat.parse(str); 208 } catch (ParseException e) { 209 return (java.util.Date) ctxt.handleWeirdStringValue(handledType(), str, 210 "expected format \"%s\"", _formatString); 211 } 212 } 213 } 214 } 215 return super._parseDate(p, ctxt); 216 } 217 } 218 219 /* 220 /********************************************************** 221 /* Deserializer implementations for Date types 222 /********************************************************** 223 */ 224 225 @JacksonStdImpl 226 public static class CalendarDeserializer extends DateBasedDeserializer<Calendar> 227 { 228 /** 229 * We may know actual expected type; if so, it will be 230 * used for instantiation. 231 * 232 * @since 2.9 233 */ 234 protected final Constructor<Calendar> _defaultCtor; 235 CalendarDeserializer()236 public CalendarDeserializer() { 237 super(Calendar.class); 238 _defaultCtor = null; 239 } 240 241 @SuppressWarnings("unchecked") CalendarDeserializer(Class<? extends Calendar> cc)242 public CalendarDeserializer(Class<? extends Calendar> cc) { 243 super(cc); 244 _defaultCtor = (Constructor<Calendar>) ClassUtil.findConstructor(cc, false); 245 } 246 CalendarDeserializer(CalendarDeserializer src, DateFormat df, String formatString)247 public CalendarDeserializer(CalendarDeserializer src, DateFormat df, String formatString) { 248 super(src, df, formatString); 249 _defaultCtor = src._defaultCtor; 250 } 251 252 @Override withDateFormat(DateFormat df, String formatString)253 protected CalendarDeserializer withDateFormat(DateFormat df, String formatString) { 254 return new CalendarDeserializer(this, df, formatString); 255 } 256 257 @Override // since 2.12 getEmptyValue(DeserializationContext ctxt)258 public Object getEmptyValue(DeserializationContext ctxt) { 259 GregorianCalendar cal = new GregorianCalendar(); 260 cal.setTimeInMillis(0L); 261 return cal; 262 } 263 264 @Override deserialize(JsonParser p, DeserializationContext ctxt)265 public Calendar deserialize(JsonParser p, DeserializationContext ctxt) throws IOException 266 { 267 Date d = _parseDate(p, ctxt); 268 if (d == null) { 269 return null; 270 } 271 if (_defaultCtor == null) { 272 return ctxt.constructCalendar(d); 273 } 274 try { 275 Calendar c = _defaultCtor.newInstance(); 276 c.setTimeInMillis(d.getTime()); 277 TimeZone tz = ctxt.getTimeZone(); 278 if (tz != null) { 279 c.setTimeZone(tz); 280 } 281 return c; 282 } catch (Exception e) { 283 return (Calendar) ctxt.handleInstantiationProblem(handledType(), d, e); 284 } 285 } 286 } 287 288 /** 289 * Simple deserializer for handling {@link java.util.Date} values. 290 *<p> 291 * One way to customize Date formats accepted is to override method 292 * {@link DeserializationContext#parseDate} that this basic 293 * deserializer calls. 294 */ 295 @JacksonStdImpl 296 public static class DateDeserializer extends DateBasedDeserializer<Date> 297 { 298 public final static DateDeserializer instance = new DateDeserializer(); 299 DateDeserializer()300 public DateDeserializer() { super(Date.class); } DateDeserializer(DateDeserializer base, DateFormat df, String formatString)301 public DateDeserializer(DateDeserializer base, DateFormat df, String formatString) { 302 super(base, df, formatString); 303 } 304 305 @Override withDateFormat(DateFormat df, String formatString)306 protected DateDeserializer withDateFormat(DateFormat df, String formatString) { 307 return new DateDeserializer(this, df, formatString); 308 } 309 310 @Override // since 2.12 getEmptyValue(DeserializationContext ctxt)311 public Object getEmptyValue(DeserializationContext ctxt) { 312 return new Date(0L); 313 } 314 315 @Override deserialize(JsonParser p, DeserializationContext ctxt)316 public java.util.Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { 317 return _parseDate(p, ctxt); 318 } 319 } 320 321 /** 322 * Compared to plain old {@link java.util.Date}, SQL version is easier 323 * to deal with: mostly because it is more limited. 324 */ 325 public static class SqlDateDeserializer 326 extends DateBasedDeserializer<java.sql.Date> 327 { SqlDateDeserializer()328 public SqlDateDeserializer() { super(java.sql.Date.class); } SqlDateDeserializer(SqlDateDeserializer src, DateFormat df, String formatString)329 public SqlDateDeserializer(SqlDateDeserializer src, DateFormat df, String formatString) { 330 super(src, df, formatString); 331 } 332 333 @Override withDateFormat(DateFormat df, String formatString)334 protected SqlDateDeserializer withDateFormat(DateFormat df, String formatString) { 335 return new SqlDateDeserializer(this, df, formatString); 336 } 337 338 @Override // since 2.12 getEmptyValue(DeserializationContext ctxt)339 public Object getEmptyValue(DeserializationContext ctxt) { 340 return new java.sql.Date(0L); 341 } 342 343 @Override deserialize(JsonParser p, DeserializationContext ctxt)344 public java.sql.Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { 345 Date d = _parseDate(p, ctxt); 346 return (d == null) ? null : new java.sql.Date(d.getTime()); 347 } 348 } 349 350 /** 351 * Simple deserializer for handling {@link java.sql.Timestamp} values. 352 *<p> 353 * One way to customize Timestamp formats accepted is to override method 354 * {@link DeserializationContext#parseDate} that this basic 355 * deserializer calls. 356 */ 357 public static class TimestampDeserializer extends DateBasedDeserializer<Timestamp> 358 { TimestampDeserializer()359 public TimestampDeserializer() { super(Timestamp.class); } TimestampDeserializer(TimestampDeserializer src, DateFormat df, String formatString)360 public TimestampDeserializer(TimestampDeserializer src, DateFormat df, String formatString) { 361 super(src, df, formatString); 362 } 363 364 @Override withDateFormat(DateFormat df, String formatString)365 protected TimestampDeserializer withDateFormat(DateFormat df, String formatString) { 366 return new TimestampDeserializer(this, df, formatString); 367 } 368 369 @Override // since 2.12 getEmptyValue(DeserializationContext ctxt)370 public Object getEmptyValue(DeserializationContext ctxt) { 371 return new Timestamp(0L); 372 } 373 374 @Override deserialize(JsonParser p, DeserializationContext ctxt)375 public java.sql.Timestamp deserialize(JsonParser p, DeserializationContext ctxt) throws IOException 376 { 377 Date d = _parseDate(p, ctxt); 378 return (d == null) ? null : new Timestamp(d.getTime()); 379 } 380 } 381 } 382