1 package com.fasterxml.jackson.databind.deser.std; 2 3 import java.io.IOException; 4 import java.util.*; 5 6 import com.fasterxml.jackson.annotation.JsonFormat; 7 import com.fasterxml.jackson.annotation.Nulls; 8 9 import com.fasterxml.jackson.core.*; 10 import com.fasterxml.jackson.core.JsonParser.NumberType; 11 import com.fasterxml.jackson.core.exc.InputCoercionException; 12 import com.fasterxml.jackson.core.io.NumberInput; 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.cfg.CoercionInputShape; 18 import com.fasterxml.jackson.databind.deser.BeanDeserializerBase; 19 import com.fasterxml.jackson.databind.deser.NullValueProvider; 20 import com.fasterxml.jackson.databind.deser.SettableBeanProperty; 21 import com.fasterxml.jackson.databind.deser.ValueInstantiator; 22 import com.fasterxml.jackson.databind.deser.impl.NullsAsEmptyProvider; 23 import com.fasterxml.jackson.databind.deser.impl.NullsConstantProvider; 24 import com.fasterxml.jackson.databind.deser.impl.NullsFailProvider; 25 import com.fasterxml.jackson.databind.introspect.AnnotatedMember; 26 import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; 27 import com.fasterxml.jackson.databind.type.LogicalType; 28 import com.fasterxml.jackson.databind.util.AccessPattern; 29 import com.fasterxml.jackson.databind.util.ClassUtil; 30 import com.fasterxml.jackson.databind.util.Converter; 31 32 /** 33 * Base class for common deserializers. Contains shared 34 * base functionality for dealing with primitive values, such 35 * as (re)parsing from String. 36 */ 37 public abstract class StdDeserializer<T> 38 extends JsonDeserializer<T> 39 implements java.io.Serializable, 40 ValueInstantiator.Gettable // since 2.12 41 { 42 private static final long serialVersionUID = 1L; 43 44 /** 45 * Bitmask that covers {@link DeserializationFeature#USE_BIG_INTEGER_FOR_INTS} 46 * and {@link DeserializationFeature#USE_LONG_FOR_INTS}, used for more efficient 47 * cheks when coercing integral values for untyped deserialization. 48 * 49 * @since 2.6 50 */ 51 protected final static int F_MASK_INT_COERCIONS = 52 DeserializationFeature.USE_BIG_INTEGER_FOR_INTS.getMask() 53 | DeserializationFeature.USE_LONG_FOR_INTS.getMask(); 54 55 @Deprecated // since 2.12 56 protected final static int F_MASK_ACCEPT_ARRAYS = 57 DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS.getMask() | 58 DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT.getMask(); 59 60 /** 61 * Type of values this deserializer handles: sometimes 62 * exact types, other time most specific supertype of 63 * types deserializer handles (which may be as generic 64 * as {@link Object} in some case) 65 */ 66 final protected Class<?> _valueClass; 67 68 final protected JavaType _valueType; 69 StdDeserializer(Class<?> vc)70 protected StdDeserializer(Class<?> vc) { 71 _valueClass = vc; 72 _valueType = null; 73 } 74 StdDeserializer(JavaType valueType)75 protected StdDeserializer(JavaType valueType) { 76 // 26-Sep-2017, tatu: [databind#1764] need to add null-check back until 3.x 77 _valueClass = (valueType == null) ? Object.class : valueType.getRawClass(); 78 _valueType = valueType; 79 } 80 81 /** 82 * Copy-constructor for sub-classes to use, most often when creating 83 * new instances for {@link com.fasterxml.jackson.databind.deser.ContextualDeserializer}. 84 * 85 * @since 2.5 86 */ StdDeserializer(StdDeserializer<?> src)87 protected StdDeserializer(StdDeserializer<?> src) { 88 _valueClass = src._valueClass; 89 _valueType = src._valueType; 90 } 91 92 /* 93 /********************************************************** 94 /* Accessors 95 /********************************************************** 96 */ 97 98 @Override handledType()99 public Class<?> handledType() { return _valueClass; } 100 101 /* 102 /********************************************************** 103 /* Extended API 104 /********************************************************** 105 */ 106 107 /** 108 * @deprecated Since 2.3 use {@link #handledType} instead 109 */ 110 @Deprecated getValueClass()111 public final Class<?> getValueClass() { return _valueClass; } 112 113 /** 114 * Exact structured type this deserializer handles, if known. 115 */ getValueType()116 public JavaType getValueType() { return _valueType; } 117 118 /** 119 * Convenience method for getting handled type as {@link JavaType}, regardless 120 * of whether deserializer has one already resolved (and accessible via 121 * {@link #getValueType()}) or not: equivalent to: 122 *<pre> 123 * if (getValueType() != null) { 124 * return getValueType(); 125 * } 126 * return ctxt.constructType(handledType()); 127 *</pre> 128 * 129 * @since 2.10 130 */ getValueType(DeserializationContext ctxt)131 public JavaType getValueType(DeserializationContext ctxt) { 132 if (_valueType != null) { 133 return _valueType; 134 } 135 return ctxt.constructType(_valueClass); 136 } 137 138 /** 139 * @since 2.12 140 */ 141 @Override // for ValueInstantiator.Gettable getValueInstantiator()142 public ValueInstantiator getValueInstantiator() { return null; } 143 144 /** 145 * Method that can be called to determine if given deserializer is the default 146 * deserializer Jackson uses; as opposed to a custom deserializer installed by 147 * a module or calling application. Determination is done using 148 * {@link JacksonStdImpl} annotation on deserializer class. 149 */ isDefaultDeserializer(JsonDeserializer<?> deserializer)150 protected boolean isDefaultDeserializer(JsonDeserializer<?> deserializer) { 151 return ClassUtil.isJacksonStdImpl(deserializer); 152 } 153 isDefaultKeyDeserializer(KeyDeserializer keyDeser)154 protected boolean isDefaultKeyDeserializer(KeyDeserializer keyDeser) { 155 return ClassUtil.isJacksonStdImpl(keyDeser); 156 } 157 158 /* 159 /********************************************************** 160 /* Partial JsonDeserializer implementation 161 /********************************************************** 162 */ 163 164 /** 165 * Base implementation that does not assume specific type 166 * inclusion mechanism. Sub-classes are expected to override 167 * this method if they are to handle type information. 168 */ 169 @Override deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer)170 public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, 171 TypeDeserializer typeDeserializer) throws IOException { 172 return typeDeserializer.deserializeTypedFromAny(p, ctxt); 173 } 174 175 /* 176 /********************************************************************** 177 /* High-level handling of secondary input shapes (with possible coercion) 178 /********************************************************************** 179 */ 180 181 /** 182 * Helper method that allows easy support for array-related coercion features: 183 * checks for either empty array, or single-value array-wrapped value (if coercion 184 * enabled by {@code CoercionConfigs} (since 2.12), and either reports 185 * an exception (if no coercion allowed), or returns appropriate 186 * result value using coercion mechanism indicated. 187 *<p> 188 * This method should NOT be called if Array representation is explicitly supported 189 * for type: it should only be called in case it is otherwise unrecognized. 190 *<p> 191 * NOTE: in case of unwrapped single element, will handle actual decoding 192 * by calling {@link #_deserializeWrappedValue}, which by default calls 193 * {@link #deserialize(JsonParser, DeserializationContext)}. 194 * 195 * @since 2.9 196 */ 197 @SuppressWarnings("unchecked") _deserializeFromArray(JsonParser p, DeserializationContext ctxt)198 protected T _deserializeFromArray(JsonParser p, DeserializationContext ctxt) throws IOException 199 { 200 final CoercionAction act = _findCoercionFromEmptyArray(ctxt); 201 final boolean unwrap = ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); 202 203 if (unwrap || (act != CoercionAction.Fail)) { 204 JsonToken t = p.nextToken(); 205 if (t == JsonToken.END_ARRAY) { 206 switch (act) { 207 case AsEmpty: 208 return (T) getEmptyValue(ctxt); 209 case AsNull: 210 case TryConvert: 211 return getNullValue(ctxt); 212 default: 213 } 214 } else if (unwrap) { 215 final T parsed = _deserializeWrappedValue(p, ctxt); 216 if (p.nextToken() != JsonToken.END_ARRAY) { 217 handleMissingEndArrayForSingle(p, ctxt); 218 } 219 return parsed; 220 } 221 } 222 return (T) ctxt.handleUnexpectedToken(getValueType(ctxt), JsonToken.START_ARRAY, p, null); 223 } 224 225 /** 226 * Helper method that may be used to support fallback for Empty String / Empty Array 227 * non-standard representations; usually for things serialized as JSON Objects. 228 * 229 * @since 2.5 230 * 231 * @deprecated Since 2.12 232 */ 233 @SuppressWarnings("unchecked") 234 @Deprecated // since 2.12 _deserializeFromEmpty(JsonParser p, DeserializationContext ctxt)235 protected T _deserializeFromEmpty(JsonParser p, DeserializationContext ctxt) 236 throws IOException 237 { 238 if (p.hasToken(JsonToken.START_ARRAY)) { 239 if (ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT)) { 240 JsonToken t = p.nextToken(); 241 if (t == JsonToken.END_ARRAY) { 242 return null; 243 } 244 return (T) ctxt.handleUnexpectedToken(getValueType(ctxt), p); 245 } 246 } 247 return (T) ctxt.handleUnexpectedToken(getValueType(ctxt), p); 248 } 249 250 /** 251 * Helper method to call in case deserializer does not support native automatic 252 * use of incoming String values, but there may be standard coercions to consider. 253 * 254 * @since 2.12 255 */ 256 @SuppressWarnings("unchecked") _deserializeFromString(JsonParser p, DeserializationContext ctxt)257 protected T _deserializeFromString(JsonParser p, DeserializationContext ctxt) 258 throws IOException 259 { 260 final ValueInstantiator inst = getValueInstantiator(); 261 final Class<?> rawTargetType = handledType(); 262 String value = p.getValueAsString(); 263 264 if ((inst != null) && inst.canCreateFromString()) { 265 return (T) inst.createFromString(ctxt, value); 266 } 267 268 if (value.length() == 0) { 269 final CoercionAction act = ctxt.findCoercionAction(logicalType(), rawTargetType, 270 CoercionInputShape.EmptyString); 271 return (T) _deserializeFromEmptyString(p, ctxt, act, rawTargetType, 272 "empty String (\"\")"); 273 } 274 if (_isBlank(value)) { 275 final CoercionAction act = ctxt.findCoercionFromBlankString(logicalType(), rawTargetType, 276 CoercionAction.Fail); 277 return (T) _deserializeFromEmptyString(p, ctxt, act, rawTargetType, 278 "blank String (all whitespace)"); 279 } 280 281 // 28-Sep-2011, tatu: Ok this is not clean at all; but since there are legacy 282 // systems that expect conversions in some cases, let's just add a minimal 283 // patch (note: same could conceivably be used for numbers too). 284 if (inst != null) { 285 value = value.trim(); // mostly to avoid problems wrt XML indentation 286 if (inst.canCreateFromInt()) { 287 if (ctxt.findCoercionAction(LogicalType.Integer, Integer.class, 288 CoercionInputShape.String) == CoercionAction.TryConvert) { 289 return (T) inst.createFromInt(ctxt, _parseIntPrimitive(ctxt, value)); 290 } 291 } 292 if (inst.canCreateFromLong()) { 293 if (ctxt.findCoercionAction(LogicalType.Integer, Long.class, 294 CoercionInputShape.String) == CoercionAction.TryConvert) { 295 return (T) inst.createFromLong(ctxt, _parseLongPrimitive(ctxt, value)); 296 } 297 } 298 if (inst.canCreateFromBoolean()) { 299 // 29-May-2020, tatu: With 2.12 can and should use CoercionConfig so: 300 if (ctxt.findCoercionAction(LogicalType.Boolean, Boolean.class, 301 CoercionInputShape.String) == CoercionAction.TryConvert) { 302 String str = value.trim(); 303 if ("true".equals(str)) { 304 return (T) inst.createFromBoolean(ctxt, true); 305 } 306 if ("false".equals(str)) { 307 return (T) inst.createFromBoolean(ctxt, false); 308 } 309 } 310 } 311 } 312 return (T) ctxt.handleMissingInstantiator(rawTargetType, inst, ctxt.getParser(), 313 "no String-argument constructor/factory method to deserialize from String value ('%s')", 314 value); 315 } 316 _deserializeFromEmptyString(JsonParser p, DeserializationContext ctxt, CoercionAction act, Class<?> rawTargetType, String desc)317 protected Object _deserializeFromEmptyString(JsonParser p, DeserializationContext ctxt, 318 CoercionAction act, Class<?> rawTargetType, String desc) throws IOException 319 { 320 switch (act) { 321 case AsEmpty: 322 return getEmptyValue(ctxt); 323 case TryConvert: 324 // hmmmh... empty or null, typically? Assume "as null" for now 325 case AsNull: 326 return null; 327 case Fail: 328 break; 329 } 330 final ValueInstantiator inst = getValueInstantiator(); 331 332 // 03-Jun-2020, tatu: Should ideally call `handleUnexpectedToken()` instead, but 333 // since this call was already made, use it. 334 return ctxt.handleMissingInstantiator(rawTargetType, inst, p, 335 "Cannot deserialize value of type %s from %s (no String-argument constructor/factory method; coercion not enabled)", 336 ClassUtil.getTypeDescription(getValueType(ctxt)), desc); 337 } 338 339 /** 340 * Helper called to support {@link DeserializationFeature#UNWRAP_SINGLE_VALUE_ARRAYS}: 341 * default implementation simply calls 342 * {@link #deserialize(JsonParser, DeserializationContext)}, 343 * but handling may be overridden. 344 * 345 * @since 2.9 346 */ _deserializeWrappedValue(JsonParser p, DeserializationContext ctxt)347 protected T _deserializeWrappedValue(JsonParser p, DeserializationContext ctxt) throws IOException 348 { 349 // 23-Mar-2017, tatu: Let's specifically block recursive resolution to avoid 350 // either supporting nested arrays, or to cause infinite looping. 351 if (p.hasToken(JsonToken.START_ARRAY)) { 352 String msg = String.format( 353 "Cannot deserialize instance of %s out of %s token: nested Arrays not allowed with %s", 354 ClassUtil.nameOf(_valueClass), JsonToken.START_ARRAY, 355 "DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS"); 356 @SuppressWarnings("unchecked") 357 T result = (T) ctxt.handleUnexpectedToken(getValueType(ctxt), p.currentToken(), p, msg); 358 return result; 359 } 360 return (T) deserialize(p, ctxt); 361 } 362 363 /* 364 /********************************************************** 365 /* Helper methods for sub-classes, parsing: while mostly 366 /* useful for numeric types, can be also useful for dealing 367 /* with things serialized as numbers (such as Dates). 368 /********************************************************** 369 */ 370 371 @Deprecated // since 2.12, use overloaded variant that does NOT take target type _parseBooleanPrimitive(DeserializationContext ctxt, JsonParser p, Class<?> targetType)372 protected final boolean _parseBooleanPrimitive(DeserializationContext ctxt, 373 JsonParser p, Class<?> targetType) throws IOException { 374 return _parseBooleanPrimitive(p, ctxt); 375 } 376 377 /** 378 * @param ctxt Deserialization context for accessing configuration 379 * @param p Underlying parser 380 * @param targetType Actual type that is being deserialized, typically 381 * same as {@link #handledType}, and not necessarily {@code boolean} 382 * (may be {@code boolean[]} or {@code AtomicBoolean} for example); 383 * used for coercion config access 384 */ _parseBooleanPrimitive(JsonParser p, DeserializationContext ctxt)385 protected final boolean _parseBooleanPrimitive(JsonParser p, DeserializationContext ctxt) 386 throws IOException 387 { 388 String text; 389 switch (p.currentTokenId()) { 390 case JsonTokenId.ID_STRING: 391 text = p.getText(); 392 break; 393 case JsonTokenId.ID_NUMBER_INT: 394 // may accept ints too, (0 == false, otherwise true) 395 396 // call returns `null`, Boolean.TRUE or Boolean.FALSE so: 397 return _coerceBooleanFromInt(p, ctxt, Boolean.TYPE) == Boolean.TRUE; 398 case JsonTokenId.ID_TRUE: // usually caller should have handled but: 399 return true; 400 case JsonTokenId.ID_FALSE: 401 return false; 402 case JsonTokenId.ID_NULL: // null fine for non-primitive 403 _verifyNullForPrimitive(ctxt); 404 return false; 405 // 29-Jun-2020, tatu: New! "Scalar from Object" (mostly for XML) 406 case JsonTokenId.ID_START_OBJECT: 407 text = ctxt.extractScalarFromObject(p, this, Boolean.TYPE); 408 break; 409 case JsonTokenId.ID_START_ARRAY: 410 // 12-Jun-2020, tatu: For some reason calling `_deserializeFromArray()` won't work so: 411 if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) { 412 p.nextToken(); 413 final boolean parsed = _parseBooleanPrimitive(p, ctxt); 414 _verifyEndArrayForSingle(p, ctxt); 415 return parsed; 416 } 417 // fall through 418 default: 419 return ((Boolean) ctxt.handleUnexpectedToken(Boolean.TYPE, p)).booleanValue(); 420 } 421 422 final CoercionAction act = _checkFromStringCoercion(ctxt, text, 423 LogicalType.Boolean, Boolean.TYPE); 424 if (act == CoercionAction.AsNull) { 425 _verifyNullForPrimitive(ctxt); 426 return false; 427 } 428 if (act == CoercionAction.AsEmpty) { 429 return false; 430 } 431 text = text.trim(); 432 final int len = text.length(); 433 434 // For [databind#1852] allow some case-insensitive matches (namely, 435 // true/True/TRUE, false/False/FALSE 436 if (len == 4) { 437 if (_isTrue(text)) { 438 return true; 439 } 440 } else if (len == 5) { 441 if (_isFalse(text)) { 442 return false; 443 } 444 } 445 if (_hasTextualNull(text)) { 446 _verifyNullForPrimitiveCoercion(ctxt, text); 447 return false; 448 } 449 Boolean b = (Boolean) ctxt.handleWeirdStringValue(Boolean.TYPE, text, 450 "only \"true\"/\"True\"/\"TRUE\" or \"false\"/\"False\"/\"FALSE\" recognized"); 451 return Boolean.TRUE.equals(b); 452 } 453 454 // [databind#1852] _isTrue(String text)455 protected boolean _isTrue(String text) { 456 char c = text.charAt(0); 457 if (c == 't') { 458 return "true".equals(text); 459 } 460 if (c == 'T') { 461 return "TRUE".equals(text) || "True".equals(text); 462 } 463 return false; 464 } 465 _isFalse(String text)466 protected boolean _isFalse(String text) { 467 char c = text.charAt(0); 468 if (c == 'f') { 469 return "false".equals(text); 470 } 471 if (c == 'F') { 472 return "FALSE".equals(text) || "False".equals(text); 473 } 474 return false; 475 } 476 477 /** 478 * Helper method called for cases where non-primitive, boolean-based value 479 * is to be deserialized: result of this method will be {@link java.lang.Boolean}, 480 * although actual target type may be something different. 481 *<p> 482 * Note: does NOT dynamically access "empty value" or "null value" of deserializer 483 * since those values could be of type other than {@link java.lang.Boolean}. 484 * Caller may need to translate from 3 possible result types into appropriately 485 * matching output types. 486 * 487 * @param p Underlying parser 488 * @param ctxt Deserialization context for accessing configuration 489 * @param targetType Actual type that is being deserialized, may be 490 * same as {@link #handledType} but could be {@code AtomicBoolean} for example. 491 * Used for coercion config access. 492 * 493 * @since 2.12 494 */ _parseBoolean(JsonParser p, DeserializationContext ctxt, Class<?> targetType)495 protected final Boolean _parseBoolean(JsonParser p, DeserializationContext ctxt, 496 Class<?> targetType) 497 throws IOException 498 { 499 String text; 500 switch (p.currentTokenId()) { 501 case JsonTokenId.ID_STRING: 502 text = p.getText(); 503 break; 504 case JsonTokenId.ID_NUMBER_INT: 505 // may accept ints too, (0 == false, otherwise true) 506 return _coerceBooleanFromInt(p, ctxt, targetType); 507 case JsonTokenId.ID_TRUE: 508 return true; 509 case JsonTokenId.ID_FALSE: 510 return false; 511 case JsonTokenId.ID_NULL: // null fine for non-primitive 512 return null; 513 // 29-Jun-2020, tatu: New! "Scalar from Object" (mostly for XML) 514 case JsonTokenId.ID_START_OBJECT: 515 text = ctxt.extractScalarFromObject(p, this, targetType); 516 break; 517 case JsonTokenId.ID_START_ARRAY: // unwrapping / from-empty-array coercion? 518 return (Boolean) _deserializeFromArray(p, ctxt); 519 default: 520 return (Boolean) ctxt.handleUnexpectedToken(targetType, p); 521 } 522 523 final CoercionAction act = _checkFromStringCoercion(ctxt, text, 524 LogicalType.Boolean, targetType); 525 if (act == CoercionAction.AsNull) { 526 return null; 527 } 528 if (act == CoercionAction.AsEmpty) { 529 return false; 530 } 531 text = text.trim(); 532 final int len = text.length(); 533 534 // For [databind#1852] allow some case-insensitive matches (namely, 535 // true/True/TRUE, false/False/FALSE 536 if (len == 4) { 537 if (_isTrue(text)) { 538 return true; 539 } 540 } else if (len == 5) { 541 if (_isFalse(text)) { 542 return false; 543 } 544 } 545 if (_checkTextualNull(ctxt, text)) { 546 return null; 547 } 548 return (Boolean) ctxt.handleWeirdStringValue(targetType, text, 549 "only \"true\" or \"false\" recognized"); 550 } 551 _parseBytePrimitive(JsonParser p, DeserializationContext ctxt)552 protected final byte _parseBytePrimitive(JsonParser p, DeserializationContext ctxt) 553 throws IOException 554 { 555 String text; 556 switch (p.currentTokenId()) { 557 case JsonTokenId.ID_STRING: 558 text = p.getText(); 559 break; 560 case JsonTokenId.ID_NUMBER_FLOAT: 561 CoercionAction act = _checkFloatToIntCoercion(p, ctxt, Byte.TYPE); 562 if (act == CoercionAction.AsNull) { 563 return (byte) 0; 564 } 565 if (act == CoercionAction.AsEmpty) { 566 return (byte) 0; 567 } 568 return p.getByteValue(); 569 case JsonTokenId.ID_NUMBER_INT: 570 return p.getByteValue(); 571 case JsonTokenId.ID_NULL: 572 _verifyNullForPrimitive(ctxt); 573 return (byte) 0; 574 // 29-Jun-2020, tatu: New! "Scalar from Object" (mostly for XML) 575 case JsonTokenId.ID_START_OBJECT: 576 text = ctxt.extractScalarFromObject(p, this, Byte.TYPE); 577 break; 578 case JsonTokenId.ID_START_ARRAY: // unwrapping / from-empty-array coercion? 579 // 12-Jun-2020, tatu: For some reason calling `_deserializeFromArray()` won't work so: 580 if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) { 581 p.nextToken(); 582 final byte parsed = _parseBytePrimitive(p, ctxt); 583 _verifyEndArrayForSingle(p, ctxt); 584 return parsed; 585 } 586 // fall through 587 default: 588 return ((Byte) ctxt.handleUnexpectedToken(ctxt.constructType(Byte.TYPE), p)).byteValue(); 589 } 590 591 // Coercion from String 592 CoercionAction act = _checkFromStringCoercion(ctxt, text, 593 LogicalType.Integer, Byte.TYPE); 594 if (act == CoercionAction.AsNull) { 595 return (byte) 0; // no need to check as does not come from `null`, explicit coercion 596 } 597 if (act == CoercionAction.AsEmpty) { 598 return (byte) 0; 599 } 600 text = text.trim(); 601 if (_hasTextualNull(text)) { 602 _verifyNullForPrimitiveCoercion(ctxt, text); 603 return (byte) 0; 604 } 605 int value; 606 try { 607 value = NumberInput.parseInt(text); 608 } catch (IllegalArgumentException iae) { 609 return (Byte) ctxt.handleWeirdStringValue(_valueClass, text, 610 "not a valid `byte` value"); 611 } 612 // So far so good: but does it fit? Allow both -128 / 255 range (inclusive) 613 if (_byteOverflow(value)) { 614 return (Byte) ctxt.handleWeirdStringValue(_valueClass, text, 615 "overflow, value cannot be represented as 8-bit value"); 616 } 617 return (byte) value; 618 } 619 _parseShortPrimitive(JsonParser p, DeserializationContext ctxt)620 protected final short _parseShortPrimitive(JsonParser p, DeserializationContext ctxt) 621 throws IOException 622 { 623 String text; 624 switch (p.currentTokenId()) { 625 case JsonTokenId.ID_STRING: 626 text = p.getText(); 627 break; 628 case JsonTokenId.ID_NUMBER_FLOAT: 629 CoercionAction act = _checkFloatToIntCoercion(p, ctxt, Short.TYPE); 630 if (act == CoercionAction.AsNull) { 631 return (short) 0; 632 } 633 if (act == CoercionAction.AsEmpty) { 634 return (short) 0; 635 } 636 return p.getShortValue(); 637 case JsonTokenId.ID_NUMBER_INT: 638 return p.getShortValue(); 639 case JsonTokenId.ID_NULL: 640 _verifyNullForPrimitive(ctxt); 641 return (short) 0; 642 // 29-Jun-2020, tatu: New! "Scalar from Object" (mostly for XML) 643 case JsonTokenId.ID_START_OBJECT: 644 text = ctxt.extractScalarFromObject(p, this, Short.TYPE); 645 break; 646 case JsonTokenId.ID_START_ARRAY: 647 // 12-Jun-2020, tatu: For some reason calling `_deserializeFromArray()` won't work so: 648 if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) { 649 p.nextToken(); 650 final short parsed = _parseShortPrimitive(p, ctxt); 651 _verifyEndArrayForSingle(p, ctxt); 652 return parsed; 653 } 654 // fall through to fail 655 default: 656 return ((Short) ctxt.handleUnexpectedToken(ctxt.constructType(Short.TYPE), p)).shortValue(); 657 } 658 659 CoercionAction act = _checkFromStringCoercion(ctxt, text, 660 LogicalType.Integer, Short.TYPE); 661 if (act == CoercionAction.AsNull) { 662 return (short) 0; // no need to check as does not come from `null`, explicit coercion 663 } 664 if (act == CoercionAction.AsEmpty) { 665 return (short) 0; 666 } 667 text = text.trim(); 668 if (_hasTextualNull(text)) { 669 _verifyNullForPrimitiveCoercion(ctxt, text); 670 return (short) 0; 671 } 672 int value; 673 try { 674 value = NumberInput.parseInt(text); 675 } catch (IllegalArgumentException iae) { 676 return (Short) ctxt.handleWeirdStringValue(Short.TYPE, text, 677 "not a valid `short` value"); 678 } 679 if (_shortOverflow(value)) { 680 return (Short) ctxt.handleWeirdStringValue(Short.TYPE, text, 681 "overflow, value cannot be represented as 16-bit value"); 682 } 683 return (short) value; 684 } 685 _parseIntPrimitive(JsonParser p, DeserializationContext ctxt)686 protected final int _parseIntPrimitive(JsonParser p, DeserializationContext ctxt) 687 throws IOException 688 { 689 String text; 690 switch (p.currentTokenId()) { 691 case JsonTokenId.ID_STRING: 692 text = p.getText(); 693 break; 694 case JsonTokenId.ID_NUMBER_FLOAT: 695 final CoercionAction act = _checkFloatToIntCoercion(p, ctxt, Integer.TYPE); 696 if (act == CoercionAction.AsNull) { 697 return 0; 698 } 699 if (act == CoercionAction.AsEmpty) { 700 return 0; 701 } 702 return p.getValueAsInt(); 703 case JsonTokenId.ID_NUMBER_INT: 704 return p.getIntValue(); 705 case JsonTokenId.ID_NULL: 706 _verifyNullForPrimitive(ctxt); 707 return 0; 708 // 29-Jun-2020, tatu: New! "Scalar from Object" (mostly for XML) 709 case JsonTokenId.ID_START_OBJECT: 710 text = ctxt.extractScalarFromObject(p, this, Integer.TYPE); 711 break; 712 case JsonTokenId.ID_START_ARRAY: 713 if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) { 714 p.nextToken(); 715 final int parsed = _parseIntPrimitive(p, ctxt); 716 _verifyEndArrayForSingle(p, ctxt); 717 return parsed; 718 } 719 // fall through to fail 720 default: 721 return ((Number) ctxt.handleUnexpectedToken(Integer.TYPE, p)).intValue(); 722 } 723 724 final CoercionAction act = _checkFromStringCoercion(ctxt, text, 725 LogicalType.Integer, Integer.TYPE); 726 if (act == CoercionAction.AsNull) { 727 return 0; // no need to check as does not come from `null`, explicit coercion 728 } 729 if (act == CoercionAction.AsEmpty) { 730 return 0; 731 } 732 text = text.trim(); 733 if (_hasTextualNull(text)) { 734 _verifyNullForPrimitiveCoercion(ctxt, text); 735 return 0; 736 } 737 return _parseIntPrimitive(ctxt, text); 738 } 739 740 /** 741 * @since 2.9 742 */ _parseIntPrimitive(DeserializationContext ctxt, String text)743 protected final int _parseIntPrimitive(DeserializationContext ctxt, String text) throws IOException 744 { 745 try { 746 if (text.length() > 9) { 747 long l = Long.parseLong(text); 748 if (_intOverflow(l)) { 749 Number v = (Number) ctxt.handleWeirdStringValue(Integer.TYPE, text, 750 "Overflow: numeric value (%s) out of range of int (%d -%d)", 751 text, Integer.MIN_VALUE, Integer.MAX_VALUE); 752 return _nonNullNumber(v).intValue(); 753 } 754 return (int) l; 755 } 756 return NumberInput.parseInt(text); 757 } catch (IllegalArgumentException iae) { 758 Number v = (Number) ctxt.handleWeirdStringValue(Integer.TYPE, text, 759 "not a valid `int` value"); 760 return _nonNullNumber(v).intValue(); 761 } 762 } 763 _parseLongPrimitive(JsonParser p, DeserializationContext ctxt)764 protected final long _parseLongPrimitive(JsonParser p, DeserializationContext ctxt) 765 throws IOException 766 { 767 String text; 768 switch (p.currentTokenId()) { 769 case JsonTokenId.ID_STRING: 770 text = p.getText(); 771 break; 772 case JsonTokenId.ID_NUMBER_FLOAT: 773 final CoercionAction act = _checkFloatToIntCoercion(p, ctxt, Long.TYPE); 774 if (act == CoercionAction.AsNull) { 775 return 0L; 776 } 777 if (act == CoercionAction.AsEmpty) { 778 return 0L; 779 } 780 return p.getValueAsLong(); 781 case JsonTokenId.ID_NUMBER_INT: 782 return p.getLongValue(); 783 case JsonTokenId.ID_NULL: 784 _verifyNullForPrimitive(ctxt); 785 return 0L; 786 // 29-Jun-2020, tatu: New! "Scalar from Object" (mostly for XML) 787 case JsonTokenId.ID_START_OBJECT: 788 text = ctxt.extractScalarFromObject(p, this, Long.TYPE); 789 break; 790 case JsonTokenId.ID_START_ARRAY: 791 if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) { 792 p.nextToken(); 793 final long parsed = _parseLongPrimitive(p, ctxt); 794 _verifyEndArrayForSingle(p, ctxt); 795 return parsed; 796 } 797 // fall through 798 default: 799 return ((Number) ctxt.handleUnexpectedToken(Long.TYPE, p)).longValue(); 800 } 801 802 final CoercionAction act = _checkFromStringCoercion(ctxt, text, 803 LogicalType.Integer, Long.TYPE); 804 if (act == CoercionAction.AsNull) { 805 return 0L; // no need to check as does not come from `null`, explicit coercion 806 } 807 if (act == CoercionAction.AsEmpty) { 808 return 0L; 809 } 810 text = text.trim(); 811 if (_hasTextualNull(text)) { 812 _verifyNullForPrimitiveCoercion(ctxt, text); 813 return 0L; 814 } 815 return _parseLongPrimitive(ctxt, text); 816 } 817 818 /** 819 * @since 2.9 820 */ _parseLongPrimitive(DeserializationContext ctxt, String text)821 protected final long _parseLongPrimitive(DeserializationContext ctxt, String text) throws IOException 822 { 823 try { 824 return NumberInput.parseLong(text); 825 } catch (IllegalArgumentException iae) { } 826 { 827 Number v = (Number) ctxt.handleWeirdStringValue(Long.TYPE, text, 828 "not a valid `long` value"); 829 return _nonNullNumber(v).longValue(); 830 } 831 } 832 _parseFloatPrimitive(JsonParser p, DeserializationContext ctxt)833 protected final float _parseFloatPrimitive(JsonParser p, DeserializationContext ctxt) 834 throws IOException 835 { 836 String text; 837 switch (p.currentTokenId()) { 838 case JsonTokenId.ID_STRING: 839 text = p.getText(); 840 break; 841 case JsonTokenId.ID_NUMBER_INT: 842 case JsonTokenId.ID_NUMBER_FLOAT: 843 return p.getFloatValue(); 844 case JsonTokenId.ID_NULL: 845 _verifyNullForPrimitive(ctxt); 846 return 0f; 847 // 29-Jun-2020, tatu: New! "Scalar from Object" (mostly for XML) 848 case JsonTokenId.ID_START_OBJECT: 849 text = ctxt.extractScalarFromObject(p, this, Float.TYPE); 850 break; 851 case JsonTokenId.ID_START_ARRAY: 852 if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) { 853 p.nextToken(); 854 final float parsed = _parseFloatPrimitive(p, ctxt); 855 _verifyEndArrayForSingle(p, ctxt); 856 return parsed; 857 } 858 // fall through 859 default: 860 return ((Number) ctxt.handleUnexpectedToken(Float.TYPE, p)).floatValue(); 861 } 862 863 final CoercionAction act = _checkFromStringCoercion(ctxt, text, 864 LogicalType.Integer, Float.TYPE); 865 if (act == CoercionAction.AsNull) { 866 return 0.0f; // no need to check as does not come from `null`, explicit coercion 867 } 868 if (act == CoercionAction.AsEmpty) { 869 return 0.0f; 870 } 871 text = text.trim(); 872 if (_hasTextualNull(text)) { 873 _verifyNullForPrimitiveCoercion(ctxt, text); 874 return 0.0f; 875 } 876 return _parseFloatPrimitive(ctxt, text); 877 } 878 879 /** 880 * @since 2.9 881 */ _parseFloatPrimitive(DeserializationContext ctxt, String text)882 protected final float _parseFloatPrimitive(DeserializationContext ctxt, String text) 883 throws IOException 884 { 885 switch (text.charAt(0)) { 886 case 'I': 887 if (_isPosInf(text)) { 888 return Float.POSITIVE_INFINITY; 889 } 890 break; 891 case 'N': 892 if (_isNaN(text)) { return Float.NaN; } 893 break; 894 case '-': 895 if (_isNegInf(text)) { 896 return Float.NEGATIVE_INFINITY; 897 } 898 break; 899 } 900 try { 901 return Float.parseFloat(text); 902 } catch (IllegalArgumentException iae) { } 903 Number v = (Number) ctxt.handleWeirdStringValue(Float.TYPE, text, 904 "not a valid `float` value"); 905 return _nonNullNumber(v).floatValue(); 906 } 907 _parseDoublePrimitive(JsonParser p, DeserializationContext ctxt)908 protected final double _parseDoublePrimitive(JsonParser p, DeserializationContext ctxt) 909 throws IOException 910 { 911 String text; 912 switch (p.currentTokenId()) { 913 case JsonTokenId.ID_STRING: 914 text = p.getText(); 915 break; 916 case JsonTokenId.ID_NUMBER_INT: 917 case JsonTokenId.ID_NUMBER_FLOAT: 918 return p.getDoubleValue(); 919 case JsonTokenId.ID_NULL: 920 _verifyNullForPrimitive(ctxt); 921 return 0.0; 922 // 29-Jun-2020, tatu: New! "Scalar from Object" (mostly for XML) 923 case JsonTokenId.ID_START_OBJECT: 924 text = ctxt.extractScalarFromObject(p, this, Double.TYPE); 925 break; 926 case JsonTokenId.ID_START_ARRAY: 927 if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) { 928 p.nextToken(); 929 final double parsed = _parseDoublePrimitive(p, ctxt); 930 _verifyEndArrayForSingle(p, ctxt); 931 return parsed; 932 } 933 // fall through 934 default: 935 return ((Number) ctxt.handleUnexpectedToken(Double.TYPE, p)).doubleValue(); 936 } 937 938 final CoercionAction act = _checkFromStringCoercion(ctxt, text, 939 LogicalType.Integer, Double.TYPE); 940 if (act == CoercionAction.AsNull) { 941 return 0.0; // no need to check as does not come from `null`, explicit coercion 942 } 943 if (act == CoercionAction.AsEmpty) { 944 return 0.0; 945 } 946 text = text.trim(); 947 if (_hasTextualNull(text)) { 948 _verifyNullForPrimitiveCoercion(ctxt, text); 949 return 0.0; 950 } 951 return _parseDoublePrimitive(ctxt, text); 952 } 953 954 /** 955 * @since 2.9 956 */ _parseDoublePrimitive(DeserializationContext ctxt, String text)957 protected final double _parseDoublePrimitive(DeserializationContext ctxt, String text) 958 throws IOException 959 { 960 switch (text.charAt(0)) { 961 case 'I': 962 if (_isPosInf(text)) { 963 return Double.POSITIVE_INFINITY; 964 } 965 break; 966 case 'N': 967 if (_isNaN(text)) { 968 return Double.NaN; 969 } 970 break; 971 case '-': 972 if (_isNegInf(text)) { 973 return Double.NEGATIVE_INFINITY; 974 } 975 break; 976 } 977 try { 978 return _parseDouble(text); 979 } catch (IllegalArgumentException iae) { } 980 Number v = (Number) ctxt.handleWeirdStringValue(Double.TYPE, text, 981 "not a valid `double` value (as String to convert)"); 982 return _nonNullNumber(v).doubleValue(); 983 } 984 985 /** 986 * Helper method for encapsulating calls to low-level double value parsing; single place 987 * just because we need a work-around that must be applied to all calls. 988 */ _parseDouble(String numStr)989 protected final static double _parseDouble(String numStr) throws NumberFormatException 990 { 991 // avoid some nasty float representations... but should it be MIN_NORMAL or MIN_VALUE? 992 if (NumberInput.NASTY_SMALL_DOUBLE.equals(numStr)) { 993 return Double.MIN_NORMAL; // since 2.7; was MIN_VALUE prior 994 } 995 return Double.parseDouble(numStr); 996 } 997 _parseDate(JsonParser p, DeserializationContext ctxt)998 protected java.util.Date _parseDate(JsonParser p, DeserializationContext ctxt) 999 throws IOException 1000 { 1001 String text; 1002 switch (p.currentTokenId()) { 1003 case JsonTokenId.ID_STRING: 1004 text = p.getText(); 1005 break; 1006 case JsonTokenId.ID_NUMBER_INT: 1007 { 1008 long ts; 1009 try { 1010 ts = p.getLongValue(); 1011 // 16-Jan-2019, tatu: 2.10 uses InputCoercionException, earlier JsonParseException 1012 // (but leave both until 3.0) 1013 } catch (JsonParseException | InputCoercionException e) { 1014 Number v = (Number) ctxt.handleWeirdNumberValue(_valueClass, p.getNumberValue(), 1015 "not a valid 64-bit `long` for creating `java.util.Date`"); 1016 ts = v.longValue(); 1017 } 1018 return new java.util.Date(ts); 1019 } 1020 case JsonTokenId.ID_NULL: 1021 return (java.util.Date) getNullValue(ctxt); 1022 // 29-Jun-2020, tatu: New! "Scalar from Object" (mostly for XML) 1023 case JsonTokenId.ID_START_OBJECT: 1024 text = ctxt.extractScalarFromObject(p, this, _valueClass); 1025 break; 1026 case JsonTokenId.ID_START_ARRAY: 1027 return _parseDateFromArray(p, ctxt); 1028 default: 1029 return (java.util.Date) ctxt.handleUnexpectedToken(_valueClass, p); 1030 } 1031 1032 return _parseDate(text.trim(), ctxt); 1033 } 1034 1035 // @since 2.9 _parseDateFromArray(JsonParser p, DeserializationContext ctxt)1036 protected java.util.Date _parseDateFromArray(JsonParser p, DeserializationContext ctxt) 1037 throws IOException 1038 { 1039 final CoercionAction act = _findCoercionFromEmptyArray(ctxt); 1040 final boolean unwrap = ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); 1041 1042 if (unwrap || (act != CoercionAction.Fail)) { 1043 JsonToken t = p.nextToken(); 1044 if (t == JsonToken.END_ARRAY) { 1045 switch (act) { 1046 case AsEmpty: 1047 return (java.util.Date) getEmptyValue(ctxt); 1048 case AsNull: 1049 case TryConvert: 1050 return (java.util.Date) getNullValue(ctxt); 1051 default: 1052 } 1053 } else if (unwrap) { 1054 final Date parsed = _parseDate(p, ctxt); 1055 _verifyEndArrayForSingle(p, ctxt); 1056 return parsed; 1057 } 1058 } 1059 return (java.util.Date) ctxt.handleUnexpectedToken(_valueClass, JsonToken.START_ARRAY, p, null); 1060 } 1061 1062 /** 1063 * @since 2.8 1064 */ _parseDate(String value, DeserializationContext ctxt)1065 protected java.util.Date _parseDate(String value, DeserializationContext ctxt) 1066 throws IOException 1067 { 1068 try { 1069 // Take empty Strings to mean 'empty' Value, usually 'null': 1070 if (value.length() == 0) { 1071 final CoercionAction act = _checkFromStringCoercion(ctxt, value); 1072 switch (act) { // note: Fail handled above 1073 case AsEmpty: 1074 return new java.util.Date(0L); 1075 case AsNull: 1076 case TryConvert: 1077 default: 1078 } 1079 return null; 1080 } 1081 // 10-Jun-2020, tatu: Legacy handling from pre-2.12... should we still have it? 1082 if (_hasTextualNull(value)) { 1083 return null; 1084 } 1085 return ctxt.parseDate(value); 1086 } catch (IllegalArgumentException iae) { 1087 return (java.util.Date) ctxt.handleWeirdStringValue(_valueClass, value, 1088 "not a valid representation (error: %s)", 1089 ClassUtil.exceptionMessage(iae)); 1090 } 1091 } 1092 1093 /** 1094 * Helper method used for accessing String value, if possible, doing 1095 * necessary conversion or throwing exception as necessary. 1096 * 1097 * @since 2.1 1098 */ _parseString(JsonParser p, DeserializationContext ctxt)1099 protected final String _parseString(JsonParser p, DeserializationContext ctxt) throws IOException 1100 { 1101 if (p.hasToken(JsonToken.VALUE_STRING)) { 1102 return p.getText(); 1103 } 1104 // 07-Nov-2019, tatu: [databind#2535] Need to support byte[]->Base64 same as `StringDeserializer` 1105 if (p.hasToken(JsonToken.VALUE_EMBEDDED_OBJECT)) { 1106 Object ob = p.getEmbeddedObject(); 1107 if (ob instanceof byte[]) { 1108 return ctxt.getBase64Variant().encode((byte[]) ob, false); 1109 } 1110 if (ob == null) { 1111 return null; 1112 } 1113 // otherwise, try conversion using toString()... 1114 return ob.toString(); 1115 } 1116 // 29-Jun-2020, tatu: New! "Scalar from Object" (mostly for XML) 1117 if (p.hasToken(JsonToken.START_OBJECT)) { 1118 return ctxt.extractScalarFromObject(p, this, _valueClass); 1119 } 1120 1121 String value = p.getValueAsString(); 1122 if (value != null) { 1123 return value; 1124 } 1125 return (String) ctxt.handleUnexpectedToken(String.class, p); 1126 } 1127 1128 /** 1129 * Helper method called to determine if we are seeing String value of 1130 * "null", and, further, that it should be coerced to null just like 1131 * null token. 1132 * 1133 * @since 2.3 1134 */ _hasTextualNull(String value)1135 protected boolean _hasTextualNull(String value) { 1136 return "null".equals(value); 1137 } 1138 _isNegInf(String text)1139 protected final boolean _isNegInf(String text) { 1140 return "-Infinity".equals(text) || "-INF".equals(text); 1141 } 1142 _isPosInf(String text)1143 protected final boolean _isPosInf(String text) { 1144 return "Infinity".equals(text) || "INF".equals(text); 1145 } 1146 _isNaN(String text)1147 protected final boolean _isNaN(String text) { return "NaN".equals(text); } 1148 1149 // @since 2.12 _isBlank(String text)1150 protected final static boolean _isBlank(String text) 1151 { 1152 final int len = text.length(); 1153 for (int i = 0; i < len; ++i) { 1154 if (text.charAt(i) > 0x0020) { 1155 return false; 1156 } 1157 } 1158 return true; 1159 } 1160 1161 /* 1162 /**************************************************** 1163 /* Helper methods for sub-classes, new (2.12+) 1164 /**************************************************** 1165 */ 1166 1167 /** 1168 * @since 2.12 1169 */ _checkFromStringCoercion(DeserializationContext ctxt, String value)1170 protected CoercionAction _checkFromStringCoercion(DeserializationContext ctxt, String value) 1171 throws IOException 1172 { 1173 return _checkFromStringCoercion(ctxt, value, logicalType(), handledType()); 1174 } 1175 1176 /** 1177 * @since 2.12 1178 */ _checkFromStringCoercion(DeserializationContext ctxt, String value, LogicalType logicalType, Class<?> rawTargetType)1179 protected CoercionAction _checkFromStringCoercion(DeserializationContext ctxt, String value, 1180 LogicalType logicalType, Class<?> rawTargetType) 1181 throws IOException 1182 { 1183 final CoercionAction act; 1184 1185 if (value.length() == 0) { 1186 act = ctxt.findCoercionAction(logicalType, rawTargetType, 1187 CoercionInputShape.EmptyString); 1188 return _checkCoercionActionFail(ctxt, act, "empty String (\"\")"); 1189 } else if (_isBlank(value)) { 1190 act = ctxt.findCoercionFromBlankString(logicalType, rawTargetType, CoercionAction.Fail); 1191 return _checkCoercionActionFail(ctxt, act, "blank String (all whitespace)"); 1192 } else { 1193 act = ctxt.findCoercionAction(logicalType, rawTargetType, CoercionInputShape.String); 1194 if (act == CoercionAction.Fail) { 1195 // since it MIGHT (but might not), create desc here, do not use helper 1196 ctxt.reportInputMismatch(this, 1197 "Cannot coerce String value (\"%s\") to %s (but might if coercion using `CoercionConfig` was enabled)", 1198 value, _coercedTypeDesc()); 1199 } 1200 } 1201 return act; 1202 } 1203 1204 /** 1205 * @since 2.12 1206 */ _checkFloatToIntCoercion(JsonParser p, DeserializationContext ctxt, Class<?> rawTargetType)1207 protected CoercionAction _checkFloatToIntCoercion(JsonParser p, DeserializationContext ctxt, 1208 Class<?> rawTargetType) 1209 throws IOException 1210 { 1211 final CoercionAction act = ctxt.findCoercionAction(LogicalType.Integer, 1212 rawTargetType, CoercionInputShape.Float); 1213 if (act == CoercionAction.Fail) { 1214 _checkCoercionActionFail(ctxt, act, "Floating-point value ("+p.getText()+")"); 1215 } 1216 return act; 1217 } 1218 1219 /** 1220 * @since 2.12 1221 */ _coerceBooleanFromInt(JsonParser p, DeserializationContext ctxt, Class<?> rawTargetType)1222 protected Boolean _coerceBooleanFromInt(JsonParser p, DeserializationContext ctxt, 1223 Class<?> rawTargetType) 1224 throws IOException 1225 { 1226 CoercionAction act = ctxt.findCoercionAction(LogicalType.Boolean, rawTargetType, CoercionInputShape.Integer); 1227 switch (act) { 1228 case Fail: 1229 _checkCoercionActionFail(ctxt, act, "Integer value ("+p.getText()+")"); 1230 break; 1231 case AsNull: 1232 return null; 1233 case AsEmpty: 1234 return Boolean.FALSE; 1235 default: 1236 } 1237 // 13-Oct-2016, tatu: As per [databind#1324], need to be careful wrt 1238 // degenerate case of huge integers, legal in JSON. 1239 // Also note that number tokens can not have WS to trim: 1240 if (p.getNumberType() == NumberType.INT) { 1241 // but minor optimization for common case is possible: 1242 return p.getIntValue() != 0; 1243 } 1244 return !"0".equals(p.getText()); 1245 } 1246 _checkCoercionActionFail(DeserializationContext ctxt, CoercionAction act, String inputDesc)1247 protected CoercionAction _checkCoercionActionFail(DeserializationContext ctxt, 1248 CoercionAction act, String inputDesc) throws IOException 1249 { 1250 if (act == CoercionAction.Fail) { 1251 ctxt.reportInputMismatch(this, 1252 "Cannot coerce %s to %s (but could if coercion was enabled using `CoercionConfig`)", 1253 inputDesc, _coercedTypeDesc()); 1254 } 1255 return act; 1256 } 1257 1258 /** 1259 * Method called when otherwise unrecognized String value is encountered for 1260 * a non-primitive type: should see if it is String value {@code "null"}, and if so, 1261 * whether it is acceptable according to configuration or not 1262 * 1263 * @since 2.12 1264 */ _checkTextualNull(DeserializationContext ctxt, String text)1265 protected boolean _checkTextualNull(DeserializationContext ctxt, String text) 1266 throws JsonMappingException 1267 { 1268 if (_hasTextualNull(text)) { 1269 if (!ctxt.isEnabled(MapperFeature.ALLOW_COERCION_OF_SCALARS)) { 1270 _reportFailedNullCoerce(ctxt, true, MapperFeature.ALLOW_COERCION_OF_SCALARS, "String \"null\""); 1271 } 1272 return true; 1273 } 1274 return false; 1275 } 1276 1277 /* 1278 /********************************************************************** 1279 /* Helper methods for sub-classes, coercions, older (pre-2.12), non-deprecated 1280 /********************************************************************** 1281 */ 1282 1283 /** 1284 * Helper method called in case where an integral number is encountered, but 1285 * config settings suggest that a coercion may be needed to "upgrade" 1286 * {@link java.lang.Number} into "bigger" type like {@link java.lang.Long} or 1287 * {@link java.math.BigInteger} 1288 * 1289 * @see DeserializationFeature#USE_BIG_INTEGER_FOR_INTS 1290 * @see DeserializationFeature#USE_LONG_FOR_INTS 1291 * 1292 * @since 2.6 1293 */ _coerceIntegral(JsonParser p, DeserializationContext ctxt)1294 protected Object _coerceIntegral(JsonParser p, DeserializationContext ctxt) throws IOException 1295 { 1296 int feats = ctxt.getDeserializationFeatures(); 1297 if (DeserializationFeature.USE_BIG_INTEGER_FOR_INTS.enabledIn(feats)) { 1298 return p.getBigIntegerValue(); 1299 } 1300 if (DeserializationFeature.USE_LONG_FOR_INTS.enabledIn(feats)) { 1301 return p.getLongValue(); 1302 } 1303 return p.getNumberValue(); // should be optimal, whatever it is 1304 } 1305 1306 /** 1307 * Method called to verify that {@code null} token from input is acceptable 1308 * for primitive (unboxed) target type. It should NOT be called if {@code null} 1309 * was received by other means (coerced due to configuration, or even from 1310 * optionally acceptable String {@code "null"} token). 1311 * 1312 * @since 2.9 1313 */ _verifyNullForPrimitive(DeserializationContext ctxt)1314 protected final void _verifyNullForPrimitive(DeserializationContext ctxt) throws JsonMappingException 1315 { 1316 if (ctxt.isEnabled(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)) { 1317 ctxt.reportInputMismatch(this, 1318 "Cannot coerce `null` to %s (disable `DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES` to allow)", 1319 _coercedTypeDesc()); 1320 } 1321 } 1322 1323 /** 1324 * Method called to verify that text value {@code "null"} from input is acceptable 1325 * for primitive (unboxed) target type. It should not be called if actual 1326 * {@code null} token was received, or if null is a result of coercion from 1327 * Some other input type. 1328 * 1329 * @since 2.9 1330 */ _verifyNullForPrimitiveCoercion(DeserializationContext ctxt, String str)1331 protected final void _verifyNullForPrimitiveCoercion(DeserializationContext ctxt, String str) throws JsonMappingException 1332 { 1333 Enum<?> feat; 1334 boolean enable; 1335 1336 if (!ctxt.isEnabled(MapperFeature.ALLOW_COERCION_OF_SCALARS)) { 1337 feat = MapperFeature.ALLOW_COERCION_OF_SCALARS; 1338 enable = true; 1339 } else if (ctxt.isEnabled(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)) { 1340 feat = DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES; 1341 enable = false; 1342 } else { 1343 return; 1344 } 1345 String strDesc = str.isEmpty() ? "empty String (\"\")" : String.format("String \"%s\"", str); 1346 _reportFailedNullCoerce(ctxt, enable, feat, strDesc); 1347 } 1348 _reportFailedNullCoerce(DeserializationContext ctxt, boolean state, Enum<?> feature, String inputDesc)1349 protected void _reportFailedNullCoerce(DeserializationContext ctxt, boolean state, Enum<?> feature, 1350 String inputDesc) throws JsonMappingException 1351 { 1352 String enableDesc = state ? "enable" : "disable"; 1353 ctxt.reportInputMismatch(this, "Cannot coerce %s to Null value as %s (%s `%s.%s` to allow)", 1354 inputDesc, _coercedTypeDesc(), enableDesc, feature.getClass().getSimpleName(), feature.name()); 1355 } 1356 1357 /** 1358 * Helper method called to get a description of type into which a scalar value coercion 1359 * is (most likely) being applied, to be used for constructing exception messages 1360 * on coerce failure. 1361 * 1362 * @return Message with backtick-enclosed name of type this deserializer supports 1363 * 1364 * @since 2.9 1365 */ _coercedTypeDesc()1366 protected String _coercedTypeDesc() { 1367 boolean structured; 1368 String typeDesc; 1369 1370 JavaType t = getValueType(); 1371 if ((t != null) && !t.isPrimitive()) { 1372 structured = (t.isContainerType() || t.isReferenceType()); 1373 typeDesc = ClassUtil.getTypeDescription(t); 1374 } else { 1375 Class<?> cls = handledType(); 1376 structured = cls.isArray() || Collection.class.isAssignableFrom(cls) 1377 || Map.class.isAssignableFrom(cls); 1378 typeDesc = ClassUtil.getClassDescription(cls); 1379 } 1380 if (structured) { 1381 return "element of "+typeDesc; 1382 } 1383 return typeDesc+" value"; 1384 } 1385 1386 /* 1387 /********************************************************************** 1388 /* Helper methods for sub-classes, coercions, older (pre-2.12), deprecated 1389 /********************************************************************** 1390 */ 1391 1392 @Deprecated // since 2.12 _parseBooleanFromInt(JsonParser p, DeserializationContext ctxt)1393 protected boolean _parseBooleanFromInt(JsonParser p, DeserializationContext ctxt) 1394 throws IOException 1395 { 1396 // 13-Oct-2016, tatu: As per [databind#1324], need to be careful wrt 1397 // degenerate case of huge integers, legal in JSON. 1398 // ... this is, on the other hand, probably wrong/sub-optimal for non-JSON 1399 // input. For now, no rea 1400 _verifyNumberForScalarCoercion(ctxt, p); 1401 // Anyway, note that since we know it's valid (JSON) integer, it can't have 1402 // extra whitespace to trim. 1403 return !"0".equals(p.getText()); 1404 } 1405 1406 /** 1407 * @deprecated Since 2.12 use {@link #_checkFromStringCoercion} instead 1408 */ 1409 @Deprecated _verifyStringForScalarCoercion(DeserializationContext ctxt, String str)1410 protected void _verifyStringForScalarCoercion(DeserializationContext ctxt, String str) throws JsonMappingException 1411 { 1412 MapperFeature feat = MapperFeature.ALLOW_COERCION_OF_SCALARS; 1413 if (!ctxt.isEnabled(feat)) { 1414 ctxt.reportInputMismatch(this, "Cannot coerce String \"%s\" to %s (enable `%s.%s` to allow)", 1415 str, _coercedTypeDesc(), feat.getClass().getSimpleName(), feat.name()); 1416 } 1417 } 1418 1419 /** 1420 * Method called when JSON String with value "" (that is, zero length) is encountered. 1421 * 1422 * @deprecated Since 2.12 1423 */ 1424 @Deprecated _coerceEmptyString(DeserializationContext ctxt, boolean isPrimitive)1425 protected Object _coerceEmptyString(DeserializationContext ctxt, boolean isPrimitive) throws JsonMappingException 1426 { 1427 Enum<?> feat; 1428 boolean enable; 1429 1430 if (!ctxt.isEnabled(MapperFeature.ALLOW_COERCION_OF_SCALARS)) { 1431 feat = MapperFeature.ALLOW_COERCION_OF_SCALARS; 1432 enable = true; 1433 } else if (isPrimitive && ctxt.isEnabled(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)) { 1434 feat = DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES; 1435 enable = false; 1436 } else { 1437 return getNullValue(ctxt); 1438 } 1439 _reportFailedNullCoerce(ctxt, enable, feat, "empty String (\"\")"); 1440 return null; 1441 } 1442 1443 @Deprecated // since 2.12 _failDoubleToIntCoercion(JsonParser p, DeserializationContext ctxt, String type)1444 protected void _failDoubleToIntCoercion(JsonParser p, DeserializationContext ctxt, 1445 String type) throws IOException 1446 { 1447 ctxt.reportInputMismatch(handledType(), 1448 "Cannot coerce a floating-point value ('%s') into %s (enable `DeserializationFeature.ACCEPT_FLOAT_AS_INT` to allow)", 1449 p.getValueAsString(), type); 1450 } 1451 1452 @Deprecated // since 2.12 _verifyNullForScalarCoercion(DeserializationContext ctxt, String str)1453 protected final void _verifyNullForScalarCoercion(DeserializationContext ctxt, String str) throws JsonMappingException 1454 { 1455 if (!ctxt.isEnabled(MapperFeature.ALLOW_COERCION_OF_SCALARS)) { 1456 String strDesc = str.isEmpty() ? "empty String (\"\")" : String.format("String \"%s\"", str); 1457 _reportFailedNullCoerce(ctxt, true, MapperFeature.ALLOW_COERCION_OF_SCALARS, strDesc); 1458 } 1459 } 1460 1461 @Deprecated // since 2.12 _verifyNumberForScalarCoercion(DeserializationContext ctxt, JsonParser p)1462 protected void _verifyNumberForScalarCoercion(DeserializationContext ctxt, JsonParser p) throws IOException 1463 { 1464 MapperFeature feat = MapperFeature.ALLOW_COERCION_OF_SCALARS; 1465 if (!ctxt.isEnabled(feat)) { 1466 // 31-Mar-2017, tatu: Since we don't know (or this deep, care) about exact type, 1467 // access as a String: may require re-encoding by parser which should be fine 1468 String valueDesc = p.getText(); 1469 ctxt.reportInputMismatch(this, "Cannot coerce Number (%s) to %s (enable `%s.%s` to allow)", 1470 valueDesc, _coercedTypeDesc(), feat.getClass().getSimpleName(), feat.name()); 1471 } 1472 } 1473 1474 @Deprecated // since 2.12 _coerceNullToken(DeserializationContext ctxt, boolean isPrimitive)1475 protected Object _coerceNullToken(DeserializationContext ctxt, boolean isPrimitive) throws JsonMappingException 1476 { 1477 if (isPrimitive) { 1478 _verifyNullForPrimitive(ctxt); 1479 } 1480 return getNullValue(ctxt); 1481 } 1482 1483 @Deprecated // since 2.12 _coerceTextualNull(DeserializationContext ctxt, boolean isPrimitive)1484 protected Object _coerceTextualNull(DeserializationContext ctxt, boolean isPrimitive) throws JsonMappingException { 1485 if (!ctxt.isEnabled(MapperFeature.ALLOW_COERCION_OF_SCALARS)) { 1486 _reportFailedNullCoerce(ctxt, true, MapperFeature.ALLOW_COERCION_OF_SCALARS, "String \"null\""); 1487 } 1488 return getNullValue(ctxt); 1489 } 1490 1491 @Deprecated // since 2.12 _isEmptyOrTextualNull(String value)1492 protected boolean _isEmptyOrTextualNull(String value) { 1493 return value.isEmpty() || "null".equals(value); 1494 } 1495 1496 /* 1497 /**************************************************** 1498 /* Helper methods for sub-classes, resolving dependencies 1499 /**************************************************** 1500 */ 1501 1502 /** 1503 * Helper method used to locate deserializers for properties the 1504 * type this deserializer handles contains (usually for properties of 1505 * bean types) 1506 * 1507 * @param type Type of property to deserialize 1508 * @param property Actual property object (field, method, constuctor parameter) used 1509 * for passing deserialized values; provided so deserializer can be contextualized if necessary 1510 */ findDeserializer(DeserializationContext ctxt, JavaType type, BeanProperty property)1511 protected JsonDeserializer<Object> findDeserializer(DeserializationContext ctxt, 1512 JavaType type, BeanProperty property) 1513 throws JsonMappingException 1514 { 1515 return ctxt.findContextualValueDeserializer(type, property); 1516 } 1517 1518 /** 1519 * Helper method to check whether given text refers to what looks like a clean simple 1520 * integer number, consisting of optional sign followed by a sequence of digits. 1521 *<p> 1522 * Note that definition is quite loose as leading zeroes are allowed, in addition 1523 * to plus sign (not just minus). 1524 */ _isIntNumber(String text)1525 protected final boolean _isIntNumber(String text) 1526 { 1527 final int len = text.length(); 1528 if (len > 0) { 1529 char c = text.charAt(0); 1530 // skip leading sign (plus not allowed for strict JSON numbers but...) 1531 int i; 1532 1533 if (c == '-' || c == '+') { 1534 if (len == 1) { 1535 return false; 1536 } 1537 i = 1; 1538 } else { 1539 i = 0; 1540 } 1541 // We will allow leading 1542 for (; i < len; ++i) { 1543 int ch = text.charAt(i); 1544 if (ch > '9' || ch < '0') { 1545 return false; 1546 } 1547 } 1548 return true; 1549 } 1550 return false; 1551 } 1552 1553 /* 1554 /********************************************************** 1555 /* Helper methods for: deserializer construction 1556 /********************************************************** 1557 */ 1558 1559 /** 1560 * Helper method that can be used to see if specified property has annotation 1561 * indicating that a converter is to be used for contained values (contents 1562 * of structured types; array/List/Map values) 1563 * 1564 * @param existingDeserializer (optional) configured content 1565 * serializer if one already exists. 1566 * 1567 * @since 2.2 1568 */ findConvertingContentDeserializer(DeserializationContext ctxt, BeanProperty prop, JsonDeserializer<?> existingDeserializer)1569 protected JsonDeserializer<?> findConvertingContentDeserializer(DeserializationContext ctxt, 1570 BeanProperty prop, JsonDeserializer<?> existingDeserializer) 1571 throws JsonMappingException 1572 { 1573 final AnnotationIntrospector intr = ctxt.getAnnotationIntrospector(); 1574 if (_neitherNull(intr, prop)) { 1575 AnnotatedMember member = prop.getMember(); 1576 if (member != null) { 1577 Object convDef = intr.findDeserializationContentConverter(member); 1578 if (convDef != null) { 1579 Converter<Object,Object> conv = ctxt.converterInstance(prop.getMember(), convDef); 1580 JavaType delegateType = conv.getInputType(ctxt.getTypeFactory()); 1581 if (existingDeserializer == null) { 1582 existingDeserializer = ctxt.findContextualValueDeserializer(delegateType, prop); 1583 } 1584 return new StdDelegatingDeserializer<Object>(conv, delegateType, existingDeserializer); 1585 } 1586 } 1587 } 1588 return existingDeserializer; 1589 } 1590 1591 /* 1592 /********************************************************** 1593 /* Helper methods for: accessing contextual config settings 1594 /********************************************************** 1595 */ 1596 1597 /** 1598 * Helper method that may be used to find if this deserializer has specific 1599 * {@link JsonFormat} settings, either via property, or through type-specific 1600 * defaulting. 1601 * 1602 * @param typeForDefaults Type (erased) used for finding default format settings, if any 1603 * 1604 * @since 2.7 1605 */ findFormatOverrides(DeserializationContext ctxt, BeanProperty prop, Class<?> typeForDefaults)1606 protected JsonFormat.Value findFormatOverrides(DeserializationContext ctxt, 1607 BeanProperty prop, Class<?> typeForDefaults) 1608 { 1609 if (prop != null) { 1610 return prop.findPropertyFormat(ctxt.getConfig(), typeForDefaults); 1611 } 1612 // even without property or AnnotationIntrospector, may have type-specific defaults 1613 return ctxt.getDefaultPropertyFormat(typeForDefaults); 1614 } 1615 1616 /** 1617 * Convenience method that uses {@link #findFormatOverrides} to find possible 1618 * defaults and/of overrides, and then calls 1619 * <code>JsonFormat.Value.getFeature(feat)</code> 1620 * to find whether that feature has been specifically marked as enabled or disabled. 1621 * 1622 * @param typeForDefaults Type (erased) used for finding default format settings, if any 1623 * 1624 * @since 2.7 1625 */ findFormatFeature(DeserializationContext ctxt, BeanProperty prop, Class<?> typeForDefaults, JsonFormat.Feature feat)1626 protected Boolean findFormatFeature(DeserializationContext ctxt, 1627 BeanProperty prop, Class<?> typeForDefaults, JsonFormat.Feature feat) 1628 { 1629 JsonFormat.Value format = findFormatOverrides(ctxt, prop, typeForDefaults); 1630 if (format != null) { 1631 return format.getFeature(feat); 1632 } 1633 return null; 1634 } 1635 1636 /** 1637 * Method called to find {@link NullValueProvider} for a primary property, using 1638 * "value nulls" setting. If no provider found (not defined, or is "skip"), 1639 * will return `null`. 1640 * 1641 * @since 2.9 1642 */ findValueNullProvider(DeserializationContext ctxt, SettableBeanProperty prop, PropertyMetadata propMetadata)1643 protected final NullValueProvider findValueNullProvider(DeserializationContext ctxt, 1644 SettableBeanProperty prop, PropertyMetadata propMetadata) 1645 throws JsonMappingException 1646 { 1647 if (prop != null) { 1648 return _findNullProvider(ctxt, prop, propMetadata.getValueNulls(), 1649 prop.getValueDeserializer()); 1650 } 1651 return null; 1652 } 1653 1654 /** 1655 * Method called to find {@link NullValueProvider} for a contents of a structured 1656 * primary property (Collection, Map, array), using 1657 * "content nulls" setting. If no provider found (not defined), 1658 * will return given value deserializer (which is a null value provider itself). 1659 * 1660 * @since 2.9 1661 */ findContentNullProvider(DeserializationContext ctxt, BeanProperty prop, JsonDeserializer<?> valueDeser)1662 protected NullValueProvider findContentNullProvider(DeserializationContext ctxt, 1663 BeanProperty prop, JsonDeserializer<?> valueDeser) 1664 throws JsonMappingException 1665 { 1666 final Nulls nulls = findContentNullStyle(ctxt, prop); 1667 if (nulls == Nulls.SKIP) { 1668 return NullsConstantProvider.skipper(); 1669 } 1670 // 09-Dec-2019, tatu: [databind#2567] need to ensure correct target type (element, 1671 // not container), so inlined here before calling _findNullProvider 1672 if (nulls == Nulls.FAIL) { 1673 if (prop == null) { 1674 JavaType type = ctxt.constructType(valueDeser.handledType()); 1675 // should always be container? But let's double-check just in case: 1676 if (type.isContainerType()) { 1677 type = type.getContentType(); 1678 } 1679 return NullsFailProvider.constructForRootValue(type); 1680 } 1681 return NullsFailProvider.constructForProperty(prop, prop.getType().getContentType()); 1682 } 1683 1684 NullValueProvider prov = _findNullProvider(ctxt, prop, nulls, valueDeser); 1685 if (prov != null) { 1686 return prov; 1687 } 1688 return valueDeser; 1689 } 1690 findContentNullStyle(DeserializationContext ctxt, BeanProperty prop)1691 protected Nulls findContentNullStyle(DeserializationContext ctxt, BeanProperty prop) 1692 throws JsonMappingException 1693 { 1694 if (prop != null) { 1695 return prop.getMetadata().getContentNulls(); 1696 } 1697 return null; 1698 } 1699 1700 // @since 2.9 _findNullProvider(DeserializationContext ctxt, BeanProperty prop, Nulls nulls, JsonDeserializer<?> valueDeser)1701 protected final NullValueProvider _findNullProvider(DeserializationContext ctxt, 1702 BeanProperty prop, Nulls nulls, JsonDeserializer<?> valueDeser) 1703 throws JsonMappingException 1704 { 1705 if (nulls == Nulls.FAIL) { 1706 if (prop == null) { 1707 return NullsFailProvider.constructForRootValue(ctxt.constructType(valueDeser.handledType())); 1708 } 1709 return NullsFailProvider.constructForProperty(prop); 1710 } 1711 if (nulls == Nulls.AS_EMPTY) { 1712 // cannot deal with empty values if there is no value deserializer that 1713 // can indicate what "empty value" is: 1714 if (valueDeser == null) { 1715 return null; 1716 } 1717 1718 // Let's first do some sanity checking... 1719 // NOTE: although we could use `ValueInstantiator.Gettable` in general, 1720 // let's not since that would prevent being able to use custom impls: 1721 if (valueDeser instanceof BeanDeserializerBase) { 1722 ValueInstantiator vi = ((BeanDeserializerBase) valueDeser).getValueInstantiator(); 1723 if (!vi.canCreateUsingDefault()) { 1724 final JavaType type = prop.getType(); 1725 ctxt.reportBadDefinition(type, 1726 String.format("Cannot create empty instance of %s, no default Creator", type)); 1727 } 1728 } 1729 // Second: can with pre-fetch value? 1730 { 1731 AccessPattern access = valueDeser.getEmptyAccessPattern(); 1732 if (access == AccessPattern.ALWAYS_NULL) { 1733 return NullsConstantProvider.nuller(); 1734 } 1735 if (access == AccessPattern.CONSTANT) { 1736 return NullsConstantProvider.forValue(valueDeser.getEmptyValue(ctxt)); 1737 } 1738 } 1739 return new NullsAsEmptyProvider(valueDeser); 1740 } 1741 if (nulls == Nulls.SKIP) { 1742 return NullsConstantProvider.skipper(); 1743 } 1744 return null; 1745 } 1746 1747 // @since 2.12 _findCoercionFromEmptyString(DeserializationContext ctxt)1748 protected CoercionAction _findCoercionFromEmptyString(DeserializationContext ctxt) { 1749 return ctxt.findCoercionAction(logicalType(), handledType(), 1750 CoercionInputShape.EmptyString); 1751 } 1752 1753 // @since 2.12 _findCoercionFromEmptyArray(DeserializationContext ctxt)1754 protected CoercionAction _findCoercionFromEmptyArray(DeserializationContext ctxt) { 1755 return ctxt.findCoercionAction(logicalType(), handledType(), 1756 CoercionInputShape.EmptyArray); 1757 } 1758 1759 // @since 2.12 _findCoercionFromBlankString(DeserializationContext ctxt)1760 protected CoercionAction _findCoercionFromBlankString(DeserializationContext ctxt) { 1761 return ctxt.findCoercionFromBlankString(logicalType(), handledType(), 1762 CoercionAction.Fail); 1763 } 1764 1765 /* 1766 /********************************************************** 1767 /* Helper methods for sub-classes, problem reporting 1768 /********************************************************** 1769 */ 1770 1771 /** 1772 * Method called to deal with a property that did not map to a known 1773 * Bean property. Method can deal with the problem as it sees fit (ignore, 1774 * throw exception); but if it does return, it has to skip the matching 1775 * Json content parser has. 1776 * 1777 * @param p Parser that points to value of the unknown property 1778 * @param ctxt Context for deserialization; allows access to the parser, 1779 * error reporting functionality 1780 * @param instanceOrClass Instance that is being populated by this 1781 * deserializer, or if not known, Class that would be instantiated. 1782 * If null, will assume type is what {@link #getValueClass} returns. 1783 * @param propName Name of the property that cannot be mapped 1784 */ handleUnknownProperty(JsonParser p, DeserializationContext ctxt, Object instanceOrClass, String propName)1785 protected void handleUnknownProperty(JsonParser p, DeserializationContext ctxt, 1786 Object instanceOrClass, String propName) 1787 throws IOException 1788 { 1789 if (instanceOrClass == null) { 1790 instanceOrClass = handledType(); 1791 } 1792 // Maybe we have configured handler(s) to take care of it? 1793 if (ctxt.handleUnknownProperty(p, this, instanceOrClass, propName)) { 1794 return; 1795 } 1796 /* But if we do get this far, need to skip whatever value we 1797 * are pointing to now (although handler is likely to have done that already) 1798 */ 1799 p.skipChildren(); 1800 } 1801 handleMissingEndArrayForSingle(JsonParser p, DeserializationContext ctxt)1802 protected void handleMissingEndArrayForSingle(JsonParser p, DeserializationContext ctxt) 1803 throws IOException 1804 { 1805 ctxt.reportWrongTokenException(this, JsonToken.END_ARRAY, 1806 "Attempted to unwrap '%s' value from an array (with `DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS`) but it contains more than one value", 1807 handledType().getName()); 1808 // 05-May-2016, tatu: Should recover somehow (maybe skip until END_ARRAY); 1809 // but for now just fall through 1810 } 1811 _verifyEndArrayForSingle(JsonParser p, DeserializationContext ctxt)1812 protected void _verifyEndArrayForSingle(JsonParser p, DeserializationContext ctxt) throws IOException 1813 { 1814 JsonToken t = p.nextToken(); 1815 if (t != JsonToken.END_ARRAY) { 1816 handleMissingEndArrayForSingle(p, ctxt); 1817 } 1818 } 1819 1820 /* 1821 /********************************************************** 1822 /* Helper methods, other 1823 /********************************************************** 1824 */ 1825 1826 /** 1827 * @since 2.9 1828 */ _neitherNull(Object a, Object b)1829 protected final static boolean _neitherNull(Object a, Object b) { 1830 return (a != null) && (b != null); 1831 } 1832 1833 /** 1834 * @since 2.9 1835 */ _byteOverflow(int value)1836 protected final boolean _byteOverflow(int value) { 1837 // 07-nov-2016, tatu: We support "unsigned byte" as well 1838 // as Java signed range since that's relatively common usage 1839 return (value < Byte.MIN_VALUE || value > 255); 1840 } 1841 1842 /** 1843 * @since 2.9 1844 */ _shortOverflow(int value)1845 protected final boolean _shortOverflow(int value) { 1846 return (value < Short.MIN_VALUE || value > Short.MAX_VALUE); 1847 } 1848 1849 /** 1850 * @since 2.9 1851 */ _intOverflow(long value)1852 protected final boolean _intOverflow(long value) { 1853 return (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE); 1854 } 1855 1856 /** 1857 * @since 2.9 1858 */ _nonNullNumber(Number n)1859 protected Number _nonNullNumber(Number n) { 1860 if (n == null) { 1861 n = Integer.valueOf(0); 1862 } 1863 return n; 1864 } 1865 } 1866