Skip to content

Commit

Permalink
Add tests for CoercionConfig wrt int/boolean from "String" (since XML…
Browse files Browse the repository at this point in the history
… has no native token types for those)
  • Loading branch information
cowtowncoder committed Dec 18, 2020
1 parent 3ad3995 commit e1e7cce
Show file tree
Hide file tree
Showing 3 changed files with 304 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
package com.fasterxml.jackson.dataformat.xml.deser.convert;

import java.math.BigInteger;
import java.util.concurrent.atomic.AtomicLong;

import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.cfg.CoercionAction;
import com.fasterxml.jackson.databind.cfg.CoercionInputShape;
import com.fasterxml.jackson.databind.type.LogicalType;

import com.fasterxml.jackson.dataformat.xml.XmlTestBase;

// 2020-12-18, tatu: Modified from "jackson-databind" version: XML
// backend MUST NOT prevent coercion from String since XML has no
// native number representation (although TBH JsonParser.isExpectedNumberInt()
// can work around that in many cases)
public class CoerceStringToIntsTest
extends XmlTestBase
{
private final ObjectMapper DEFAULT_MAPPER = newMapper();
private final ObjectMapper MAPPER_LEGACY_FAIL = mapperBuilder()
.disable(MapperFeature.ALLOW_COERCION_OF_SCALARS)
.build();

private final ObjectMapper MAPPER_TO_EMPTY; {
MAPPER_TO_EMPTY = newMapper();
MAPPER_TO_EMPTY.coercionConfigFor(LogicalType.Integer)
.setCoercion(CoercionInputShape.String, CoercionAction.AsEmpty);
}

private final ObjectMapper MAPPER_TRY_CONVERT; {
MAPPER_TRY_CONVERT = newMapper();
MAPPER_TRY_CONVERT.coercionConfigFor(LogicalType.Integer)
.setCoercion(CoercionInputShape.String, CoercionAction.TryConvert);
}

private final ObjectMapper MAPPER_TO_NULL; {
MAPPER_TO_NULL = newMapper();
MAPPER_TO_NULL.coercionConfigFor(LogicalType.Integer)
.setCoercion(CoercionInputShape.String, CoercionAction.AsNull);
}

private final ObjectMapper MAPPER_TO_FAIL; {
MAPPER_TO_FAIL = newMapper();
MAPPER_TO_FAIL.coercionConfigFor(LogicalType.Integer)
.setCoercion(CoercionInputShape.String, CoercionAction.Fail);
}

protected static class BooleanWrapper {
public Boolean b;

public BooleanWrapper() { }
public BooleanWrapper(Boolean value) { b = value; }
}

protected static class IntWrapper {
public int i;

public IntWrapper() { }
public IntWrapper(int value) { i = value; }
}

protected static class LongWrapper {
public long l;

public LongWrapper() { }
public LongWrapper(long value) { l = value; }
}

protected static class DoubleWrapper {
public double d;

public DoubleWrapper() { }
public DoubleWrapper(double value) { d = value; }
}

/*
/********************************************************
/* Test methods, legacy setting
/********************************************************
*/

// Works by default (as per databind defaulting); but also works
// even if seemingly prevented -- this because XML has no native
// number type and Strings present all scalar values, essentially

public void testDefaultStringToIntCoercion() throws Exception {
_verifyLegacyFromStringSucceeds(DEFAULT_MAPPER);
}

public void testLegacyFailStringToInt() throws Exception {
_verifyLegacyFromStringSucceeds(MAPPER_LEGACY_FAIL);
}

private void _verifyLegacyFromStringSucceeds(ObjectMapper mapper) throws Exception
{
// by default, should be ok
Integer I = DEFAULT_MAPPER.readValue("<Integer>28</Integer>", Integer.class);
assertEquals(28, I.intValue());
{
IntWrapper w = DEFAULT_MAPPER.readValue("<IntWrapper><i>37</i></IntWrapper>",
IntWrapper.class);
assertEquals(37, w.i);
}

Long L = DEFAULT_MAPPER.readValue("<Long>39</Long>", Long.class);
assertEquals(39L, L.longValue());
{
LongWrapper w = DEFAULT_MAPPER.readValue("<LongWrapper><l>-13</l></LongWrapper>",
LongWrapper.class);
assertEquals(-13L, w.l);
}

Short S = DEFAULT_MAPPER.readValue("<Short>42</Short>", Short.class);
assertEquals(42, S.intValue());

BigInteger biggie = DEFAULT_MAPPER.readValue("<BigInteger>95007</BigInteger>", BigInteger.class);
assertEquals(95007, biggie.intValue());

AtomicLong atom = DEFAULT_MAPPER.readValue("<AtomicLong>25236</AtomicLong>", AtomicLong.class);
assertEquals(25236L, atom.get());
}

/*
/********************************************************
/* Test methods, CoerceConfig, integers-from-String
/********************************************************
*/

// When explicitly enabled, should pass

public void testCoerceConfigStringToNull() throws Exception {
_verifyCoercionFromStringSucceeds(MAPPER_TO_NULL);
}

// But even if blocked, or changed to null, should pass since with
// XML, "String" is a native representation of numbers

public void testCoerceConfigStringToEmpty() throws Exception {
_verifyCoercionFromStringSucceeds(MAPPER_TO_EMPTY);
}

public void testCoerceConfigStringConvert() throws Exception {
_verifyCoercionFromStringSucceeds(MAPPER_TRY_CONVERT);
}

public void testCoerceConfigFailFromString() throws Exception {
_verifyCoercionFromStringSucceeds(MAPPER_TO_FAIL);
}

private void _verifyCoercionFromStringSucceeds(ObjectMapper mapper) throws Exception
{
assertEquals(Integer.valueOf(12), mapper.readValue("<Integer>12</Integer>", Integer.class));
assertEquals(Integer.valueOf(34), mapper.readValue("<int>34</int>", Integer.TYPE));
{
IntWrapper w = mapper.readValue( "<IntWrapper i='-225' />", IntWrapper.class);
assertEquals(-225, w.i);
}

assertEquals(Long.valueOf(34), mapper.readValue("<Long>34</Long>", Long.class));
assertEquals(Long.valueOf(534), mapper.readValue("<long>534</long>", Long.TYPE));
{
LongWrapper w = mapper.readValue("<LongWrapper><l>-225</l></LongWrapper>",
LongWrapper.class);
assertEquals(-225L, w.l);
}

assertEquals(Short.valueOf((short)12), mapper.readValue("<Short>12</Short>", Short.class));
assertEquals(Short.valueOf((short) 344), mapper.readValue("<short>344</short>", Short.TYPE));

assertEquals(Byte.valueOf((byte)12), mapper.readValue("<Byte>12</Byte>", Byte.class));
assertEquals(Byte.valueOf((byte) -99), mapper.readValue("<byte>-99</byte>", Byte.TYPE));

assertEquals(BigInteger.valueOf(1242L),
mapper.readValue("<BigInteger>1242</BigInteger>", BigInteger.class));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package com.fasterxml.jackson.dataformat.xml.deser.convert;

import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;

import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.cfg.CoercionAction;
import com.fasterxml.jackson.databind.cfg.CoercionInputShape;
import com.fasterxml.jackson.databind.type.LogicalType;

import com.fasterxml.jackson.dataformat.xml.XmlTestBase;

// 2020-12-18, tatu: Modified from "jackson-databind" version: XML
// backend MUST NOT prevent coercion from String since XML has no
// native boolean representation
public class CoerceToBooleanTest
extends XmlTestBase
{
static class BooleanPOJO {
public boolean value;

public void setValue(boolean v) { value = v; }
}

private final ObjectMapper DEFAULT_MAPPER = newMapper();

private final ObjectMapper MAPPER_STRING_TO_BOOLEAN_FAIL; {
MAPPER_STRING_TO_BOOLEAN_FAIL = newMapper();
MAPPER_STRING_TO_BOOLEAN_FAIL.coercionConfigFor(LogicalType.Boolean)
.setCoercion(CoercionInputShape.String, CoercionAction.Fail);
}

private final ObjectMapper MAPPER_EMPTY_TO_BOOLEAN_FAIL; {
MAPPER_EMPTY_TO_BOOLEAN_FAIL = newMapper();
MAPPER_EMPTY_TO_BOOLEAN_FAIL.coercionConfigFor(LogicalType.Boolean)
.setCoercion(CoercionInputShape.EmptyString, CoercionAction.Fail);
}

/*
/**********************************************************
/* Test methods: default, legacy configuration, from String
/**********************************************************
*/

// for [databind#403]
public void testEmptyStringFailForBooleanPrimitive() throws IOException
{
final ObjectReader reader = MAPPER_EMPTY_TO_BOOLEAN_FAIL
.readerFor(BooleanPOJO.class);
try {
reader.readValue("<BooleanPOJO><value></value></BooleanPOJO>");
fail("Expected failure for boolean + empty String");
} catch (JsonMappingException e) {
verifyException(e, "Cannot coerce empty String");
verifyException(e, "to `boolean` value");
}
}

public void testDefaultStringToBooleanCoercionOk() throws Exception {
_verifyStringToBooleanOk(DEFAULT_MAPPER);
}

/*
/**********************************************************
/* Test methods: CoercionConfig, from String
/**********************************************************
*/

public void testStringToBooleanOkDespiteCoercionConfig() throws Exception {
_verifyStringToBooleanOk(MAPPER_STRING_TO_BOOLEAN_FAIL);
}

/*
/**********************************************************
/* Verification
/**********************************************************
*/

public void _verifyStringToBooleanOk(ObjectMapper mapper) throws Exception
{
// first successful coercions, basic types:
_verifyCoerceSuccess(mapper, _xmlWrapped("boolean", "true"), Boolean.TYPE, Boolean.TRUE);
_verifyCoerceSuccess(mapper, _xmlWrapped("boolean", "false"), Boolean.TYPE, Boolean.FALSE);

_verifyCoerceSuccess(mapper, _xmlWrapped("Boolean", "true"), Boolean.class, Boolean.TRUE);
_verifyCoerceSuccess(mapper, _xmlWrapped("Boolean", "false"), Boolean.class, Boolean.FALSE);

// and then allowed variants:
_verifyCoerceSuccess(mapper, _xmlWrapped("boolean", "True"), Boolean.TYPE, Boolean.TRUE);
_verifyCoerceSuccess(mapper, _xmlWrapped("Boolean", "True"), Boolean.class, Boolean.TRUE);
_verifyCoerceSuccess(mapper, _xmlWrapped("boolean", "TRUE"), Boolean.TYPE, Boolean.TRUE);
_verifyCoerceSuccess(mapper, _xmlWrapped("Boolean", "TRUE"), Boolean.class, Boolean.TRUE);
_verifyCoerceSuccess(mapper, _xmlWrapped("boolean", "False"), Boolean.TYPE, Boolean.FALSE);
_verifyCoerceSuccess(mapper, _xmlWrapped("Boolean", "False"), Boolean.class, Boolean.FALSE);
_verifyCoerceSuccess(mapper, _xmlWrapped("boolean", "FALSE"), Boolean.TYPE, Boolean.FALSE);
_verifyCoerceSuccess(mapper, _xmlWrapped("Boolean", "FALSE"), Boolean.class, Boolean.FALSE);

// and then Special boolean derivatives:
// Alas, AtomicBoolean.equals() does not work so...
final ObjectReader r = mapper.readerFor(AtomicBoolean.class);

AtomicBoolean ab = r.readValue(_xmlWrapped("AtomicBoolean", "true"));
assertTrue(ab.get());

ab = r.readValue(_xmlWrapped("AtomicBoolean", "false"));
assertFalse(ab.get());
}

/*
/**********************************************************
/* Other helper methods
/**********************************************************
*/

private String _xmlWrapped(String element, String value) {
return String.format("<%s>%s</%s>", element, value, element);
}

private void _verifyCoerceSuccess(ObjectMapper mapper,
String input, Class<?> type, Object exp) throws IOException
{
Object result = mapper.readerFor(type)
.readValue(input);
assertEquals(exp.getClass(), result.getClass());
assertEquals(exp, result);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,8 @@

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.annotation.Nulls;
import com.fasterxml.jackson.core.type.TypeReference;

import com.fasterxml.jackson.dataformat.xml.*;
Expand Down

0 comments on commit e1e7cce

Please sign in to comment.