Skip to content

Commit 1b02687

Browse files
committed
Fixed #105
1 parent 24f07b9 commit 1b02687

File tree

7 files changed

+334
-64
lines changed

7 files changed

+334
-64
lines changed

release-notes/VERSION

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ NOTE: Annotations module will never contain changes in patch versions,
1515

1616
#103: Add `JsonInclude.Include.CUSTOM`, properties for specifying filter(s) to use
1717
#104: Add new properties in `@JsonSetter`: `merge`, `null`/`contentNulls`
18+
#105: Add `@JsonFormat.lenient` to allow configuring lenience of date/time deserializers
1819
- Allow use of `@JsonView` on classes, to specify Default View to use on non-annotated
1920
properties.
2021

src/main/java/com/fasterxml/jackson/annotation/JsonFormat.java

+116-46
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,15 @@
103103
*/
104104
public String timezone() default DEFAULT_TIMEZONE;
105105

106+
/**
107+
* Property that indicates whether "lenient" handling should be enabled or
108+
* disabled. This is relevant mostly for deserialization of some textual
109+
* datatypes, especially date/time types.
110+
*
111+
* @since 2.9
112+
*/
113+
public OptBoolean lenient() default OptBoolean.DEFAULT;
114+
106115
/**
107116
* Set of {@link JsonFormat.Feature}s to explicitly enable with respect
108117
* to handling of annotated property. This will have precedence over possible
@@ -397,6 +406,11 @@ public static class Value
397406

398407
private final String _timezoneStr;
399408

409+
/**
410+
* @since 2.9
411+
*/
412+
private final Boolean _lenient;
413+
400414
/**
401415
* @since 2.6
402416
*/
@@ -406,77 +420,72 @@ public static class Value
406420
private transient TimeZone _timezone;
407421

408422
public Value() {
409-
this("", Shape.ANY, "", "", Features.empty());
423+
this("", Shape.ANY, "", "", Features.empty(), null);
410424
}
411425

412426
public Value(JsonFormat ann) {
413427
this(ann.pattern(), ann.shape(), ann.locale(), ann.timezone(),
414-
Features.construct(ann));
428+
Features.construct(ann), ann.lenient().asBoolean());
415429
}
416430

417431
/**
418-
* @since 2.6
432+
* @since 2.9
419433
*/
420-
public Value(String p, Shape sh, String localeStr, String tzStr, Features f)
434+
public Value(String p, Shape sh, String localeStr, String tzStr, Features f,
435+
Boolean lenient)
421436
{
422437
this(p, sh,
423438
(localeStr == null || localeStr.length() == 0 || DEFAULT_LOCALE.equals(localeStr)) ?
424439
null : new Locale(localeStr),
425440
(tzStr == null || tzStr.length() == 0 || DEFAULT_TIMEZONE.equals(tzStr)) ?
426441
null : tzStr,
427-
null, f);
442+
null, f, lenient);
428443
}
429444

430445
/**
431-
* @since 2.6
446+
* @since 2.9
432447
*/
433-
public Value(String p, Shape sh, Locale l, TimeZone tz, Features f)
448+
public Value(String p, Shape sh, Locale l, TimeZone tz, Features f,
449+
Boolean lenient)
434450
{
435451
_pattern = p;
436452
_shape = (sh == null) ? Shape.ANY : sh;
437453
_locale = l;
438454
_timezone = tz;
439455
_timezoneStr = null;
440456
_features = (f == null) ? Features.empty() : f;
457+
_lenient = lenient;
441458
}
442459

443460
/**
444-
* @since 2.6
461+
* @since 2.9
445462
*/
446-
public Value(String p, Shape sh, Locale l, String tzStr, TimeZone tz, Features f)
463+
public Value(String p, Shape sh, Locale l, String tzStr, TimeZone tz, Features f,
464+
Boolean lenient)
447465
{
448466
_pattern = p;
449467
_shape = (sh == null) ? Shape.ANY : sh;
450468
_locale = l;
451469
_timezone = tz;
452470
_timezoneStr = tzStr;
453471
_features = (f == null) ? Features.empty() : f;
472+
_lenient = lenient;
454473
}
455474

456-
/**
457-
* @deprecated since 2.6
458-
*/
459-
@Deprecated
460-
public Value(String p, Shape sh, Locale l, TimeZone tz) {
461-
this(p, sh, l, tz, Features.empty());
462-
}
463-
464-
/**
465-
* @deprecated since 2.6
466-
*/
467-
@Deprecated
468-
public Value(String p, Shape sh, String localeStr, String tzStr) {
469-
this(p, sh, localeStr, tzStr, Features.empty());
475+
@Deprecated // since 2.9
476+
public Value(String p, Shape sh, Locale l, String tzStr, TimeZone tz, Features f) {
477+
this(p, sh, l, tzStr, tz, f, null);
470478
}
471479

472-
/**
473-
* @deprecated since 2.6
474-
*/
475-
@Deprecated
476-
public Value(String p, Shape sh, Locale l, String tzStr, TimeZone tz) {
477-
this(p, sh, l, tzStr, tz, Features.empty());
480+
@Deprecated // since 2.9
481+
public Value(String p, Shape sh, String localeStr, String tzStr, Features f) {
482+
this(p, sh, localeStr, tzStr, f, null);
478483
}
479-
484+
@Deprecated // since 2.9
485+
public Value(String p, Shape sh, Locale l, TimeZone tz, Features f) {
486+
this(p, sh, l, tz, f, null);
487+
}
488+
480489
/**
481490
* @since 2.7
482491
*/
@@ -526,7 +535,7 @@ public final static Value from(JsonFormat ann) {
526535
* @since 2.7
527536
*/
528537
public final Value withOverrides(Value overrides) {
529-
if ((overrides == null) || (overrides == EMPTY)) {
538+
if ((overrides == null) || (overrides == EMPTY) || (overrides == this)) {
530539
return this;
531540
}
532541
if (this == EMPTY) { // cheesy, but probably common enough
@@ -550,6 +559,10 @@ public final Value withOverrides(Value overrides) {
550559
} else {
551560
f = f.withOverrides(overrides._features);
552561
}
562+
Boolean lenient = overrides._lenient;
563+
if (lenient == null) {
564+
lenient = _lenient;
565+
}
553566

554567
// timezone not merged, just choose one
555568
String tzStr = overrides._timezoneStr;
@@ -561,49 +574,75 @@ public final Value withOverrides(Value overrides) {
561574
} else {
562575
tz = overrides._timezone;
563576
}
564-
return new Value(p, sh, l, tzStr, tz, f);
577+
return new Value(p, sh, l, tzStr, tz, f, lenient);
565578
}
566579

567580
/**
568581
* @since 2.6
569582
*/
570583
public static Value forPattern(String p) {
571-
return new Value(p, null, null, null, null, Features.empty());
584+
return new Value(p, null, null, null, null, Features.empty(), null);
572585
}
573586

574587
/**
575588
* @since 2.7
576589
*/
577590
public static Value forShape(Shape sh) {
578-
return new Value(null, sh, null, null, null, Features.empty());
591+
return new Value(null, sh, null, null, null, Features.empty(), null);
579592
}
580593

594+
/**
595+
* @since 2.9
596+
*/
597+
public static Value forLeniency(boolean lenient) {
598+
return new Value(null, null, null, null, null, Features.empty(),
599+
Boolean.valueOf(lenient));
600+
}
601+
581602
/**
582603
* @since 2.1
583604
*/
584605
public Value withPattern(String p) {
585-
return new Value(p, _shape, _locale, _timezoneStr, _timezone, _features);
606+
return new Value(p, _shape, _locale, _timezoneStr, _timezone,
607+
_features, _lenient);
586608
}
587609

588610
/**
589611
* @since 2.1
590612
*/
591613
public Value withShape(Shape s) {
592-
return new Value(_pattern, s, _locale, _timezoneStr, _timezone, _features);
614+
if (s == _shape) {
615+
return this;
616+
}
617+
return new Value(_pattern, s, _locale, _timezoneStr, _timezone,
618+
_features, _lenient);
593619
}
594620

595621
/**
596622
* @since 2.1
597623
*/
598624
public Value withLocale(Locale l) {
599-
return new Value(_pattern, _shape, l, _timezoneStr, _timezone, _features);
625+
return new Value(_pattern, _shape, l, _timezoneStr, _timezone,
626+
_features, _lenient);
600627
}
601628

602629
/**
603630
* @since 2.1
604631
*/
605632
public Value withTimeZone(TimeZone tz) {
606-
return new Value(_pattern, _shape, _locale, null, tz, _features);
633+
return new Value(_pattern, _shape, _locale, null, tz,
634+
_features, _lenient);
635+
}
636+
637+
/**
638+
* @since 2.9
639+
*/
640+
public Value withLenient(Boolean lenient) {
641+
if (lenient == _lenient) {
642+
return this;
643+
}
644+
return new Value(_pattern, _shape, _locale, _timezoneStr, _timezone,
645+
_features, lenient);
607646
}
608647

609648
/**
@@ -612,7 +651,8 @@ public Value withTimeZone(TimeZone tz) {
612651
public Value withFeature(JsonFormat.Feature f) {
613652
Features newFeats = _features.with(f);
614653
return (newFeats == _features) ? this :
615-
new Value(_pattern, _shape, _locale, _timezoneStr, _timezone, newFeats);
654+
new Value(_pattern, _shape, _locale, _timezoneStr, _timezone,
655+
newFeats, _lenient);
616656
}
617657

618658
/**
@@ -621,7 +661,8 @@ public Value withFeature(JsonFormat.Feature f) {
621661
public Value withoutFeature(JsonFormat.Feature f) {
622662
Features newFeats = _features.without(f);
623663
return (newFeats == _features) ? this :
624-
new Value(_pattern, _shape, _locale, _timezoneStr, _timezone, newFeats);
664+
new Value(_pattern, _shape, _locale, _timezoneStr, _timezone,
665+
newFeats, _lenient);
625666
}
626667

627668
@Override
@@ -633,6 +674,19 @@ public Class<JsonFormat> valueFor() {
633674
public Shape getShape() { return _shape; }
634675
public Locale getLocale() { return _locale; }
635676

677+
/**
678+
* @since 2.9
679+
*/
680+
public Boolean getLenient() {
681+
return _lenient;
682+
}
683+
/**
684+
* @since 2.9
685+
*/
686+
public boolean isLenient() {
687+
return Boolean.TRUE.equals(_lenient);
688+
}
689+
636690
/**
637691
* Alternate access (compared to {@link #getTimeZone()}) which is useful
638692
* when caller just wants time zone id to convert, but not as JDK
@@ -683,6 +737,17 @@ public boolean hasTimeZone() {
683737
return (_timezone != null) || (_timezoneStr != null && !_timezoneStr.isEmpty());
684738
}
685739

740+
/**
741+
* Accessor for checking whether there is a setting for leniency.
742+
* NOTE: does NOT mean that `lenient` is `true` necessarily; just that
743+
* it has been set.
744+
*
745+
* @since 2.9
746+
*/
747+
public boolean hasLenient() {
748+
return _lenient != null;
749+
}
750+
686751
/**
687752
* Accessor for checking whether this format value has specific setting for
688753
* given feature. Result is 3-valued with either `null`, {@link Boolean#TRUE} or
@@ -708,8 +773,8 @@ public Features getFeatures() {
708773
@Override
709774
public String toString() {
710775
// !!! TODO: Features?
711-
return String.format("[pattern=%s,shape=%s,locale=%s,timezone=%s]",
712-
_pattern, _shape, _locale, _timezoneStr);
776+
return String.format("JsonFormat.Value(pattern=%s,shape=%s,lenient=%s,locale=%s,timezone=%s)",
777+
_pattern, _shape, _lenient, _locale, _timezoneStr);
713778
}
714779

715780
@Override
@@ -719,10 +784,13 @@ public int hashCode() {
719784
hash ^= _pattern.hashCode();
720785
}
721786
hash += _shape.hashCode();
787+
if (_lenient != null) {
788+
hash ^= _lenient.hashCode();
789+
}
722790
if (_locale != null) {
723-
hash ^= _locale.hashCode();
791+
hash += _locale.hashCode();
724792
}
725-
hash += _features.hashCode();
793+
hash ^= _features.hashCode();
726794
return hash;
727795
}
728796

@@ -737,7 +805,8 @@ public boolean equals(Object o) {
737805
|| !_features.equals(other._features)) {
738806
return false;
739807
}
740-
return _equal(_timezoneStr, other._timezoneStr)
808+
return _equal(_lenient, other._lenient)
809+
&& _equal(_timezoneStr, other._timezoneStr)
741810
&& _equal(_pattern, other._pattern)
742811
&& _equal(_timezone, other._timezone)
743812
&& _equal(_locale, other._locale);
@@ -747,7 +816,8 @@ private static <T> boolean _equal(T value1, T value2)
747816
{
748817
if (value1 == null) {
749818
return (value2 == null);
750-
} else if (value2 == null) {
819+
}
820+
if (value2 == null) {
751821
return false;
752822
}
753823
return value1.equals(value2);

src/main/java/com/fasterxml/jackson/annotation/JsonIgnoreProperties.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ public boolean getMerge() {
372372

373373
@Override
374374
public String toString() {
375-
return String.format("[ignored=%s,ignoreUnknown=%s,allowGetters=%s,allowSetters=%s,merge=%s]",
375+
return String.format("JsonIgnoreProperties.Value(ignored=%s,ignoreUnknown=%s,allowGetters=%s,allowSetters=%s,merge=%s)",
376376
_ignored, _ignoreUnknown, _allowGetters, _allowSetters, _merge);
377377
}
378378

src/main/java/com/fasterxml/jackson/annotation/JsonSetter.java

+5-5
Original file line numberDiff line numberDiff line change
@@ -242,11 +242,11 @@ public static Value mergeAll(Value... values)
242242
return result;
243243
}
244244

245-
public static Value forNulls(Nulls nulls) {
245+
public static Value forValueNulls(Nulls nulls) {
246246
return construct(null, nulls, Nulls.DEFAULT);
247247
}
248248

249-
public static Value forNulls(Nulls nulls, Nulls contentNulls) {
249+
public static Value forValueNulls(Nulls nulls, Nulls contentNulls) {
250250
return construct(null, nulls, contentNulls);
251251
}
252252

@@ -296,7 +296,7 @@ public Value withOverrides(Value overrides) {
296296
return construct(merge, nulls, contentNulls);
297297
}
298298

299-
public Value withNulls(Nulls nulls) {
299+
public Value withValueNulls(Nulls nulls) {
300300
if (nulls == null) {
301301
nulls = Nulls.DEFAULT;
302302
}
@@ -306,7 +306,7 @@ public Value withNulls(Nulls nulls) {
306306
return construct(_merge, nulls, _contentNulls);
307307
}
308308

309-
public Value withNulls(Nulls valueNulls, Nulls contentNulls) {
309+
public Value withValueNulls(Nulls valueNulls, Nulls contentNulls) {
310310
if (valueNulls == null) {
311311
valueNulls = Nulls.DEFAULT;
312312
}
@@ -362,7 +362,7 @@ public Class<JsonSetter> valueFor() {
362362

363363
@Override
364364
public String toString() {
365-
return String.format("@JsonSetter.Value(merge=%s,valueNulls=%s,contentNulls=%s)",
365+
return String.format("JsonSetter.Value(merge=%s,valueNulls=%s,contentNulls=%s)",
366366
_merge, _nulls, _contentNulls);
367367
}
368368

0 commit comments

Comments
 (0)