Skip to content

Commit

Permalink
Merge branch '2.12'
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Jun 12, 2020
2 parents af847cd + a25fbce commit 34e6a4e
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.util.concurrent.atomic.AtomicBoolean;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.type.LogicalType;

Expand All @@ -13,7 +14,17 @@ public class AtomicBooleanDeserializer extends StdScalarDeserializer<AtomicBoole

@Override
public AtomicBoolean deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
return new AtomicBoolean(_parseBooleanPrimitive(ctxt, p, AtomicBoolean.class));
JsonToken t = p.currentToken();
if (t == JsonToken.VALUE_TRUE) {
return new AtomicBoolean(true);
}
if (t == JsonToken.VALUE_FALSE) {
return new AtomicBoolean(false);
}
// 12-Jun-2020, tatu: May look convoluted, but need to work correctly with
// CoercionConfig
Boolean b = _parseBoolean(ctxt, p, AtomicBoolean.class);
return (b == null) ? null : new AtomicBoolean(b.booleanValue());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,10 @@ public Boolean deserialize(JsonParser p, DeserializationContext ctxt) throws IOE
if (t == JsonToken.VALUE_FALSE) {
return Boolean.FALSE;
}
return _parseBoolean(p, ctxt);
if (_primitive) {
return _parseBooleanPrimitive(ctxt, p, _valueClass);
}
return _parseBoolean(ctxt, p, _valueClass);
}

// Since we can never have type info ("natural type"; String, Boolean, Integer, Double):
Expand All @@ -225,56 +228,10 @@ public Boolean deserializeWithType(JsonParser p, DeserializationContext ctxt,
if (t == JsonToken.VALUE_FALSE) {
return Boolean.FALSE;
}
return _parseBoolean(p, ctxt);
}

protected final Boolean _parseBoolean(JsonParser p, DeserializationContext ctxt)
throws IOException
{
JsonToken t = p.currentToken();
if (t == JsonToken.VALUE_NULL) {
return (Boolean) _coerceNullToken(ctxt, _primitive);
}
if (t == JsonToken.START_ARRAY) { // unwrapping?
return _deserializeFromArray(p, ctxt);
}
// should accept ints too, (0 == false, otherwise true)
if (t == JsonToken.VALUE_NUMBER_INT) {
return _coerceBooleanFromInt(ctxt, p, Boolean.class);
}
// And finally, let's allow Strings to be converted too
if (t == JsonToken.VALUE_STRING) {
String text = p.getText();
CoercionAction act = _checkFromStringCoercion(ctxt, text);
if (act == CoercionAction.AsNull) {
return (Boolean) getNullValue(ctxt);
}
if (act == CoercionAction.AsEmpty) {
return (Boolean) getEmptyValue(ctxt);
}
text = text.trim();
// [databind#422]: Allow aliases
if ("true".equals(text) || "True".equals(text)) {
return Boolean.TRUE;
}
if ("false".equals(text) || "False".equals(text)) {
return Boolean.FALSE;
}
if (_hasTextualNull(text)) {
return (Boolean) _coerceTextualNull(ctxt, _primitive);
}
return (Boolean) ctxt.handleWeirdStringValue(_valueClass, text,
"only \"true\" or \"false\" recognized");
}
// usually caller should have handled but:
if (t == JsonToken.VALUE_TRUE) {
return Boolean.TRUE;
}
if (t == JsonToken.VALUE_FALSE) {
return Boolean.FALSE;
if (_primitive) {
return _parseBooleanPrimitive(ctxt, p, _valueClass);
}
// Otherwise, no can do:
return (Boolean) ctxt.handleUnexpectedToken(getValueType(ctxt), p);
return _parseBoolean(ctxt, p, _valueClass);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ protected final boolean _parseBooleanPrimitive(DeserializationContext ctxt,
return false;
}

// should accept ints too, (0 == false, otherwise true)
// may accept ints too, (0 == false, otherwise true)
if (t == JsonToken.VALUE_NUMBER_INT) {
Boolean b = _coerceBooleanFromInt(ctxt, p, Boolean.TYPE);
// may get `null`, Boolean.TRUE or Boolean.FALSE so:
Expand Down Expand Up @@ -357,6 +357,57 @@ protected final boolean _parseBooleanPrimitive(DeserializationContext ctxt,
return ((Boolean) ctxt.handleUnexpectedToken(ctxt.constructType(targetType), p)).booleanValue();
}

protected final Boolean _parseBoolean(DeserializationContext ctxt,
JsonParser p, Class<?> targetType)
throws IOException
{
JsonToken t = p.currentToken();
if (t == JsonToken.VALUE_NULL) {
return (Boolean) _coerceNullToken(ctxt, false);
}
// may accept ints too, (0 == false, otherwise true)
if (t == JsonToken.VALUE_NUMBER_INT) {
return _coerceBooleanFromInt(ctxt, p, Boolean.class);
}
// And finally, let's allow Strings to be converted too
if (t == JsonToken.VALUE_STRING) {
String text = p.getText();
CoercionAction act = _checkFromStringCoercion(ctxt, text,
LogicalType.Boolean, targetType);
if (act == CoercionAction.AsNull) {
return (Boolean) getNullValue(ctxt);
}
if (act == CoercionAction.AsEmpty) {
return (Boolean) getEmptyValue(ctxt);
}
text = text.trim();
// [databind#422]: Allow aliases
if ("true".equals(text) || "True".equals(text)) {
return Boolean.TRUE;
}
if ("false".equals(text) || "False".equals(text)) {
return Boolean.FALSE;
}
if (_hasTextualNull(text)) {
return (Boolean) _coerceTextualNull(ctxt, false);
}
return (Boolean) ctxt.handleWeirdStringValue(_valueClass, text,
"only \"true\" or \"false\" recognized");
}
// usually caller should have handled but:
if (t == JsonToken.VALUE_TRUE) {
return Boolean.TRUE;
}
if (t == JsonToken.VALUE_FALSE) {
return Boolean.FALSE;
}
if (t == JsonToken.START_ARRAY) { // unwrapping?
return (Boolean) _deserializeFromArray(p, ctxt);
}
// Otherwise, no can do:
return (Boolean) ctxt.handleUnexpectedToken(ctxt.constructType(targetType), p);
}

@Deprecated // since 2.12
protected boolean _parseBooleanFromInt(JsonParser p, DeserializationContext ctxt)
throws IOException
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.cfg.CoercionAction;
import com.fasterxml.jackson.databind.cfg.CoercionInputShape;
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
import com.fasterxml.jackson.databind.type.LogicalType;

public class CoerceToBooleanTest extends BaseMapTest
{
Expand All @@ -24,9 +27,29 @@ static class BooleanPOJO {
.disable(MapperFeature.ALLOW_COERCION_OF_SCALARS)
.build();

private final ObjectMapper MAPPER_TO_EMPTY = jsonMapperBuilder()
.withCoercionConfig(LogicalType.Boolean, cfg ->
cfg.setCoercion(CoercionInputShape.Integer, CoercionAction.AsEmpty))
.build();

private final ObjectMapper MAPPER_TRY_CONVERT = jsonMapperBuilder()
.withCoercionConfig(LogicalType.Boolean, cfg ->
cfg.setCoercion(CoercionInputShape.Integer, CoercionAction.TryConvert))
.build();

private final ObjectMapper MAPPER_TO_NULL = jsonMapperBuilder()
.withCoercionConfig(LogicalType.Boolean, cfg ->
cfg.setCoercion(CoercionInputShape.Integer, CoercionAction.AsNull))
.build();

private final ObjectMapper MAPPER_TO_FAIL = jsonMapperBuilder()
.withCoercionConfig(LogicalType.Boolean, cfg ->
cfg.setCoercion(CoercionInputShape.Integer, CoercionAction.Fail))
.build();

private final static String DOC_WITH_0 = aposToQuotes("{'value':0}");
private final static String DOC_WITH_1 = aposToQuotes("{'value':1}");

/*
/**********************************************************
/* Unit tests: default, legacy configuration
Expand Down Expand Up @@ -90,10 +113,98 @@ public void testToBooleanCoercionFailChars() throws Exception

/*
/**********************************************************
/* Unit tests: new CoercionConfig
/* Unit tests: new CoercionConfig, as-null, as-empty, try-coerce
/**********************************************************
*/

public void testIntToNullCoercion() throws Exception
{
assertNull(MAPPER_TO_NULL.readValue("0", Boolean.class));
assertNull(MAPPER_TO_NULL.readValue("1", Boolean.class));

// but due to coercion to `boolean`, can not return null here -- however,
// goes "1 -> false (no null for primitive) -> Boolean.FALSE
assertEquals(Boolean.FALSE, MAPPER_TO_NULL.readValue("0", Boolean.TYPE));
assertEquals(Boolean.FALSE, MAPPER_TO_NULL.readValue("1", Boolean.TYPE));

// As to AtomicBoolean: that type itself IS nullable since it's of LogicalType.Boolean so
assertNull(MAPPER_TO_NULL.readValue("0", AtomicBoolean.class));
assertNull(MAPPER_TO_NULL.readValue("1", AtomicBoolean.class));

BooleanPOJO p;
p = MAPPER_TO_NULL.readValue(DOC_WITH_0, BooleanPOJO.class);
assertFalse(p.value);
p = MAPPER_TO_NULL.readValue(DOC_WITH_1, BooleanPOJO.class);
assertFalse(p.value);
}

public void testIntToEmptyCoercion() throws Exception
{
// "empty" value for Boolean/boolean is False/false

assertEquals(Boolean.FALSE, MAPPER_TO_EMPTY.readValue("0", Boolean.class));
assertEquals(Boolean.FALSE, MAPPER_TO_EMPTY.readValue("1", Boolean.class));

assertEquals(Boolean.FALSE, MAPPER_TO_EMPTY.readValue("0", Boolean.TYPE));
assertEquals(Boolean.FALSE, MAPPER_TO_EMPTY.readValue("1", Boolean.TYPE));

AtomicBoolean ab;
ab = MAPPER_TO_EMPTY.readValue("0", AtomicBoolean.class);
assertFalse(ab.get());
ab = MAPPER_TO_EMPTY.readValue("1", AtomicBoolean.class);
assertFalse(ab.get());

BooleanPOJO p;
p = MAPPER_TO_EMPTY.readValue(DOC_WITH_0, BooleanPOJO.class);
assertFalse(p.value);
p = MAPPER_TO_EMPTY.readValue(DOC_WITH_1, BooleanPOJO.class);
assertFalse(p.value);
}

public void testIntToTryCoercion() throws Exception
{
// And "TryCoerce" should do what would be typically expected

assertEquals(Boolean.FALSE, MAPPER_TRY_CONVERT.readValue("0", Boolean.class));
assertEquals(Boolean.TRUE, MAPPER_TRY_CONVERT.readValue("1", Boolean.class));

assertEquals(Boolean.FALSE, MAPPER_TRY_CONVERT.readValue("0", Boolean.TYPE));
assertEquals(Boolean.TRUE, MAPPER_TRY_CONVERT.readValue("1", Boolean.TYPE));

AtomicBoolean ab;
ab = MAPPER_TRY_CONVERT.readValue("0", AtomicBoolean.class);
assertFalse(ab.get());
ab = MAPPER_TRY_CONVERT.readValue("1", AtomicBoolean.class);
assertTrue(ab.get());

BooleanPOJO p;
p = MAPPER_TRY_CONVERT.readValue(DOC_WITH_0, BooleanPOJO.class);
assertFalse(p.value);
p = MAPPER_TRY_CONVERT.readValue(DOC_WITH_1, BooleanPOJO.class);
assertTrue(p.value);
}

/*
/**********************************************************
/* Unit tests: new CoercionConfig, failing
/**********************************************************
*/

public void testFailFromInteger() throws Exception
{
_verifyFailFromInteger(MAPPER_TO_FAIL, BooleanPOJO.class, DOC_WITH_0, Boolean.TYPE);
_verifyFailFromInteger(MAPPER_TO_FAIL, BooleanPOJO.class, DOC_WITH_1, Boolean.TYPE);

_verifyFailFromInteger(MAPPER_TO_FAIL, Boolean.class, "0");
_verifyFailFromInteger(MAPPER_TO_FAIL, Boolean.class, "42");

_verifyFailFromInteger(MAPPER_TO_FAIL, Boolean.TYPE, "0");
_verifyFailFromInteger(MAPPER_TO_FAIL, Boolean.TYPE, "999");

_verifyFailFromInteger(MAPPER_TO_FAIL, AtomicBoolean.class, "0");
_verifyFailFromInteger(MAPPER_TO_FAIL, AtomicBoolean.class, "-123");
}

/*
/**********************************************************
/* Helper methods
Expand Down Expand Up @@ -139,4 +250,20 @@ private void _verifyBooleanCoerceFailReason(MismatchedInputException e,
+" not as expected ("+quote(tokenValue)+")");
}
}

private void _verifyFailFromInteger(ObjectMapper m, Class<?> targetType, String doc) throws Exception {
_verifyFailFromInteger(m, targetType, doc, targetType);
}

private void _verifyFailFromInteger(ObjectMapper m, Class<?> targetType, String doc,
Class<?> valueType) throws Exception
{
try {
m.readerFor(targetType).readValue(doc);
fail("Should not accept Integer for "+targetType.getName()+" by default");
} catch (MismatchedInputException e) {
verifyException(e, "Cannot coerce Integer value");
verifyException(e, "to `"+valueType.getName()+"`");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -699,7 +699,7 @@ public void testEmptyStringFailForBooleanPrimitive() throws IOException
reader.readValue(aposToQuotes("{'booleanValue':''}"));
fail("Expected failure for boolean + empty String");
} catch (JsonMappingException e) {
verifyException(e, "Cannot map `null` into type");
verifyException(e, "Cannot coerce `null` to `boolean`");
verifyException(e, "FAIL_ON_NULL_FOR_PRIMITIVES");
}
}
Expand Down

0 comments on commit 34e6a4e

Please sign in to comment.