From ab9413aec6de9c50a77b716fccd0c198d98a6951 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Mon, 19 Aug 2019 19:12:06 -0700 Subject: Fix #2424 --- release-notes/VERSION-2.x | 1 + .../fasterxml/jackson/databind/ObjectMapper.java | 8 ++ .../jackson/databind/cfg/ConfigOverrides.java | 97 ++++++++++++++++++---- .../jackson/databind/cfg/MapperConfigBase.java | 9 +- .../deser/std/JsonLocationInstantiator.java | 2 + .../jackson/databind/node/ContainerNode.java | 2 + .../jackson/databind/node/NumericNode.java | 2 + .../fasterxml/jackson/databind/node/ValueNode.java | 2 + .../deser/jdk/DateDeserializationTest.java | 38 ++++++++- 9 files changed, 136 insertions(+), 25 deletions(-) diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 8a2fc57b9..c43e028aa 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -18,6 +18,7 @@ Project: jackson-databind to `handleUnknownVanilla()` (proposed by Vladimir T, follow up to #822) #2416: Optimize `ValueInstantiator` construction for default `Collection`, `Map` types +#2425: Add global config override setting for `@JsonFormat.lenient()` 2.10.0.pr1 (19-Jul-2019) diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java index 564854905..457f3825b 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java +++ b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java @@ -1526,6 +1526,14 @@ public class ObjectMapper return this; } + /** + * @since 2.10 + */ + public ObjectMapper setDefaultLeniency(Boolean b) { + _configOverrides.setDefaultLeniency(b); + return this; + } + /* /********************************************************** /* Subtype registration diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/ConfigOverrides.java b/src/main/java/com/fasterxml/jackson/databind/cfg/ConfigOverrides.java index 49c622cc6..dfc87e80e 100644 --- a/src/main/java/com/fasterxml/jackson/databind/cfg/ConfigOverrides.java +++ b/src/main/java/com/fasterxml/jackson/databind/cfg/ConfigOverrides.java @@ -2,6 +2,7 @@ package com.fasterxml.jackson.databind.cfg; import java.util.*; +import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonSetter; import com.fasterxml.jackson.databind.introspect.VisibilityChecker; @@ -43,10 +44,20 @@ public class ConfigOverrides */ protected Boolean _defaultMergeable; + /** + * Global default setting (if any) for leniency: if disabled ({link Boolean#TRUE}), + * "strict" (not lenient): default setting if absence of value is considered "lenient" + * in Jackson 2.x. Default setting may be overridden by per-type and per-property + * settings. + * + * @since 2.10 + */ + protected Boolean _defaultLeniency; + /* - /********************************************************** + /********************************************************************** /* Life cycle - /********************************************************** + /********************************************************************** */ public ConfigOverrides() { @@ -55,22 +66,35 @@ public class ConfigOverrides JsonInclude.Value.empty(), JsonSetter.Value.empty(), VisibilityChecker.Std.defaultInstance(), - null + null, null ); } + /** + * @since 2.10 + */ protected ConfigOverrides(Map, MutableConfigOverride> overrides, - JsonInclude.Value defIncl, - JsonSetter.Value defSetter, - VisibilityChecker defVisibility, - Boolean defMergeable) { + JsonInclude.Value defIncl, JsonSetter.Value defSetter, + VisibilityChecker defVisibility, Boolean defMergeable, Boolean defLeniency) + { _overrides = overrides; _defaultInclusion = defIncl; _defaultSetterInfo = defSetter; _visibilityChecker = defVisibility; _defaultMergeable = defMergeable; + _defaultLeniency = defLeniency; } + /** + * @deprecated Since 2.10 + */ + @Deprecated // since 2.10 + protected ConfigOverrides(Map, MutableConfigOverride> overrides, + JsonInclude.Value defIncl, JsonSetter.Value defSetter, + VisibilityChecker defVisibility, Boolean defMergeable) { + this(overrides, defIncl, defSetter, defVisibility, defMergeable, null); + } + public ConfigOverrides copy() { Map, MutableConfigOverride> newOverrides; @@ -83,15 +107,16 @@ public class ConfigOverrides } } return new ConfigOverrides(newOverrides, - _defaultInclusion, _defaultSetterInfo, _visibilityChecker, _defaultMergeable); + _defaultInclusion, _defaultSetterInfo, _visibilityChecker, + _defaultMergeable, _defaultLeniency); } /* - /********************************************************** + /********************************************************************** /* Per-type override access - /********************************************************** + /********************************************************************** */ - + public ConfigOverride findOverride(Class type) { if (_overrides == null) { return null; @@ -111,10 +136,38 @@ public class ConfigOverrides return override; } + /** + * Specific accessor for finding {code JsonFormat.Value} for given type, + * considering global default for leniency as well as per-type format + * override (if any). + * + * @return Default format settings for type; never null. + * + * @since 2.10 + */ + public JsonFormat.Value findFormatDefaults(Class type) { + if (_overrides != null) { + ConfigOverride override = _overrides.get(type); + if (override != null) { + JsonFormat.Value format = override.getFormat(); + if (format != null) { + if (!format.hasLenient()) { + return format.withLenient(_defaultLeniency); + } + return format; + } + } + } + if (_defaultLeniency == null) { + return JsonFormat.Value.empty(); + } + return JsonFormat.Value.forLeniency(_defaultLeniency); + } + /* - /********************************************************** + /********************************************************************** /* Global defaults access - /********************************************************** + /********************************************************************** */ public JsonInclude.Value getDefaultInclusion() { @@ -129,6 +182,13 @@ public class ConfigOverrides return _defaultMergeable; } + /** + * @since 2.10 + */ + public Boolean getDefaultLeniency() { + return _defaultLeniency; + } + /** * @since 2.9 */ @@ -157,6 +217,13 @@ public class ConfigOverrides _defaultMergeable = v; } + /** + * @since 2.10 + */ + public void setDefaultLeniency(Boolean v) { + _defaultLeniency = v; + } + /** * @since 2.9 */ @@ -165,9 +232,9 @@ public class ConfigOverrides } /* - /********************************************************** + /********************************************************************** /* Helper methods - /********************************************************** + /********************************************************************** */ protected Map, MutableConfigOverride> _newMap() { diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfigBase.java b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfigBase.java index a7ca2e5ad..044d13bcf 100644 --- a/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfigBase.java +++ b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfigBase.java @@ -637,14 +637,7 @@ public abstract class MapperConfigBase type) { - ConfigOverride overrides = _configOverrides.findOverride(type); - if (overrides != null) { - JsonFormat.Value v = overrides.getFormat(); - if (v != null) { - return v; - } - } - return EMPTY_FORMAT; + return _configOverrides.findFormatDefaults(type); } @Override diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/JsonLocationInstantiator.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/JsonLocationInstantiator.java index be32d08d2..ac47b1abf 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/JsonLocationInstantiator.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/JsonLocationInstantiator.java @@ -18,6 +18,8 @@ import com.fasterxml.jackson.databind.deser.ValueInstantiator; public class JsonLocationInstantiator extends ValueInstantiator.Base { + private static final long serialVersionUID = 1L; + public JsonLocationInstantiator() { super(JsonLocation.class); } diff --git a/src/main/java/com/fasterxml/jackson/databind/node/ContainerNode.java b/src/main/java/com/fasterxml/jackson/databind/node/ContainerNode.java index cf6653785..919219c13 100644 --- a/src/main/java/com/fasterxml/jackson/databind/node/ContainerNode.java +++ b/src/main/java/com/fasterxml/jackson/databind/node/ContainerNode.java @@ -15,6 +15,8 @@ public abstract class ContainerNode> extends BaseJsonNode implements JsonNodeCreator { + private static final long serialVersionUID = 1L; + /** * We will keep a reference to the Object (usually TreeMapper) * that can construct instances of nodes to add to this container diff --git a/src/main/java/com/fasterxml/jackson/databind/node/NumericNode.java b/src/main/java/com/fasterxml/jackson/databind/node/NumericNode.java index a70a6b1f9..d131e61db 100644 --- a/src/main/java/com/fasterxml/jackson/databind/node/NumericNode.java +++ b/src/main/java/com/fasterxml/jackson/databind/node/NumericNode.java @@ -11,6 +11,8 @@ import com.fasterxml.jackson.core.JsonParser; public abstract class NumericNode extends ValueNode { + private static final long serialVersionUID = 1L; + protected NumericNode() { } @Override diff --git a/src/main/java/com/fasterxml/jackson/databind/node/ValueNode.java b/src/main/java/com/fasterxml/jackson/databind/node/ValueNode.java index 322fe0294..d62023a8f 100644 --- a/src/main/java/com/fasterxml/jackson/databind/node/ValueNode.java +++ b/src/main/java/com/fasterxml/jackson/databind/node/ValueNode.java @@ -17,6 +17,8 @@ import com.fasterxml.jackson.databind.jsontype.TypeSerializer; public abstract class ValueNode extends BaseJsonNode { + private static final long serialVersionUID = 1L; + protected ValueNode() { } @Override diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/DateDeserializationTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/DateDeserializationTest.java index 5ec830eb2..ef2c88867 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/DateDeserializationTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/DateDeserializationTest.java @@ -725,7 +725,7 @@ public class DateDeserializationTest /********************************************************** */ - public void testLenientCalendar() throws Exception + public void testLenientJDKDateTypes() throws Exception { final String JSON = aposToQuotes("{'value':'2015-11-32'}"); @@ -743,8 +743,10 @@ public class DateDeserializationTest verifyException(e, "from String \"2015-11-32\""); verifyException(e, "expected format"); } + } - // similarly with Date... + public void testLenientJDKDateTypesViaTypeOverride() throws Exception + { ObjectMapper mapper = new ObjectMapper(); mapper.configOverride(java.util.Date.class) .setFormat(JsonFormat.Value.forLeniency(Boolean.FALSE)); @@ -758,6 +760,38 @@ public class DateDeserializationTest } } + public void testLenientJDKDateTypesViaGlobal() throws Exception + { + final String JSON = quote("2015-11-32"); + + // with lenient, can parse fine + Calendar value = MAPPER.readValue(JSON, Calendar.class); + assertEquals(Calendar.DECEMBER, value.get(Calendar.MONTH)); + assertEquals(2, value.get(Calendar.DAY_OF_MONTH)); + + // but not so if default leniency disabled + ObjectMapper mapper = new ObjectMapper(); + mapper.setDefaultLeniency(false); + try { + mapper.readValue(JSON, java.util.Date.class); + fail("Should not pass with invalid (with strict) date value"); + } catch (MismatchedInputException e) { + verifyException(e, "Cannot deserialize value of type `java.util.Date`"); + verifyException(e, "from String \"2015-11-32\""); + verifyException(e, "expected format"); + } + + // Unless we actually had per-type override too + mapper = new ObjectMapper(); + mapper.configOverride(Calendar.class) + .setFormat(JsonFormat.Value.forLeniency(Boolean.TRUE)); + mapper.setDefaultLeniency(false); + + value = mapper.readValue(JSON, Calendar.class); + assertEquals(Calendar.DECEMBER, value.get(Calendar.MONTH)); + assertEquals(2, value.get(Calendar.DAY_OF_MONTH)); + } + /* /********************************************************** /* Tests to verify failing cases -- cgit v1.2.3