Skip to content

Commit

Permalink
Fix #2424
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Aug 20, 2019
1 parent c326c13 commit ab9413a
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 25 deletions.
1 change: 1 addition & 0 deletions release-notes/VERSION-2.x
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1526,6 +1526,14 @@ public ObjectMapper setDefaultMergeable(Boolean b) {
return this;
}

/**
* @since 2.10
*/
public ObjectMapper setDefaultLeniency(Boolean b) {
_configOverrides.setDefaultLeniency(b);
return this;
}

/*
/**********************************************************
/* Subtype registration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

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;
Expand Down Expand Up @@ -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() {
Expand All @@ -55,22 +66,35 @@ public ConfigOverrides() {
JsonInclude.Value.empty(),
JsonSetter.Value.empty(),
VisibilityChecker.Std.defaultInstance(),
null
null, null
);
}

/**
* @since 2.10
*/
protected ConfigOverrides(Map<Class<?>, 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<Class<?>, MutableConfigOverride> overrides,
JsonInclude.Value defIncl, JsonSetter.Value defSetter,
VisibilityChecker<?> defVisibility, Boolean defMergeable) {
this(overrides, defIncl, defSetter, defVisibility, defMergeable, null);
}

public ConfigOverrides copy()
{
Map<Class<?>, MutableConfigOverride> newOverrides;
Expand All @@ -83,15 +107,16 @@ public ConfigOverrides copy()
}
}
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;
Expand All @@ -111,10 +136,38 @@ public MutableConfigOverride findOrCreateOverride(Class<?> type) {
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() {
Expand All @@ -129,6 +182,13 @@ public Boolean getDefaultMergeable() {
return _defaultMergeable;
}

/**
* @since 2.10
*/
public Boolean getDefaultLeniency() {
return _defaultLeniency;
}

/**
* @since 2.9
*/
Expand Down Expand Up @@ -157,6 +217,13 @@ public void setDefaultMergeable(Boolean v) {
_defaultMergeable = v;
}

/**
* @since 2.10
*/
public void setDefaultLeniency(Boolean v) {
_defaultLeniency = v;
}

/**
* @since 2.9
*/
Expand All @@ -165,9 +232,9 @@ public void setDefaultVisibility(VisibilityChecker<?> v) {
}

/*
/**********************************************************
/**********************************************************************
/* Helper methods
/**********************************************************
/**********************************************************************
*/

protected Map<Class<?>, MutableConfigOverride> _newMap() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -637,14 +637,7 @@ public final JsonInclude.Value getDefaultInclusion(Class<?> baseType,

@Override
public final JsonFormat.Value getDefaultPropertyFormat(Class<?> 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
public class JsonLocationInstantiator
extends ValueInstantiator.Base
{
private static final long serialVersionUID = 1L;

public JsonLocationInstantiator() {
super(JsonLocation.class);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public abstract class ContainerNode<T extends ContainerNode<T>>
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
public abstract class NumericNode
extends ValueNode
{
private static final long serialVersionUID = 1L;

protected NumericNode() { }

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
public abstract class ValueNode
extends BaseJsonNode
{
private static final long serialVersionUID = 1L;

protected ValueNode() { }

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -725,7 +725,7 @@ public void testCalendarArrayUnwrap() throws Exception
/**********************************************************
*/

public void testLenientCalendar() throws Exception
public void testLenientJDKDateTypes() throws Exception
{
final String JSON = aposToQuotes("{'value':'2015-11-32'}");

Expand All @@ -743,8 +743,10 @@ public void testLenientCalendar() throws Exception
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));
Expand All @@ -758,6 +760,38 @@ public void testLenientCalendar() throws Exception
}
}

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
Expand Down

0 comments on commit ab9413a

Please sign in to comment.