diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java index f71bdb6f5c..b4f12842d4 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java +++ b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java @@ -1198,10 +1198,11 @@ public T readValue(JsonParser jp, TypeReference valueTypeRef) * {@link TypeFactory}. */ @Override + @SuppressWarnings("unchecked") public final T readValue(JsonParser jp, ResolvedType valueType) throws IOException, JsonParseException, JsonMappingException { - return readValue(jp, (JavaType) valueType); + return (T) _readValue(copyDeserializationConfig(), jp, (JavaType) valueType); } /** diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java b/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java index 3da02c68ef..67d8d1d007 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java +++ b/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java @@ -355,8 +355,9 @@ public T readValue(JsonParser jp, TypeReference valueTypeRef) * was specified with {@link #withValueToUpdate(Object)}. */ @Override + @SuppressWarnings("unchecked") public T readValue(JsonParser jp, ResolvedType valueType) throws IOException, JsonProcessingException { - return readValue(jp, (JavaType) valueType); + return (T) withType((JavaType)valueType).readValue(jp); } /** diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDeserializer.java index 76a7152c60..e7fbe7621f 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDeserializer.java @@ -4,6 +4,7 @@ import java.util.*; import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.JsonParser.NumberType; import com.fasterxml.jackson.core.io.NumberInput; import com.fasterxml.jackson.databind.*; @@ -102,7 +103,11 @@ protected final boolean _parseBooleanPrimitive(JsonParser jp, DeserializationCon } // [JACKSON-78]: should accept ints too, (0 == false, otherwise true) if (t == JsonToken.VALUE_NUMBER_INT) { - return (jp.getIntValue() != 0); + // 11-Jan-2012, tatus: May be outside of int... + if (jp.getNumberType() == NumberType.INT) { + return (jp.getIntValue() != 0); + } + return _parseBooleanFromNumber(jp, ctxt); } // And finally, let's allow Strings to be converted too if (t == JsonToken.VALUE_STRING) { @@ -131,7 +136,11 @@ protected final Boolean _parseBoolean(JsonParser jp, DeserializationContext ctxt } // [JACKSON-78]: should accept ints too, (0 == false, otherwise true) if (t == JsonToken.VALUE_NUMBER_INT) { - return (jp.getIntValue() == 0) ? Boolean.FALSE : Boolean.TRUE; + // 11-Jan-2012, tatus: May be outside of int... + if (jp.getNumberType() == NumberType.INT) { + return (jp.getIntValue() == 0) ? Boolean.FALSE : Boolean.TRUE; + } + return Boolean.valueOf(_parseBooleanFromNumber(jp, ctxt)); } if (t == JsonToken.VALUE_NULL) { return (Boolean) getNullValue(); @@ -154,6 +163,20 @@ protected final Boolean _parseBoolean(JsonParser jp, DeserializationContext ctxt throw ctxt.mappingException(_valueClass, t); } + protected final boolean _parseBooleanFromNumber(JsonParser jp, DeserializationContext ctxt) + throws IOException, JsonProcessingException + { + if (jp.getNumberType() == NumberType.LONG) { + return (jp.getLongValue() == 0L) ? Boolean.FALSE : Boolean.TRUE; + } + // no really good logic; let's actually resort to textual comparison + String str = jp.getText(); + if ("0.0".equals(str) || "0".equals(str)) { + return Boolean.FALSE; + } + return Boolean.TRUE; + } + protected Byte _parseByte(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestJdkTypes.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestJdkTypes.java index c2795c1a0c..a9af7ef441 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/TestJdkTypes.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestJdkTypes.java @@ -7,10 +7,10 @@ import java.util.Locale; import java.util.regex.Pattern; +import com.fasterxml.jackson.annotation.*; import com.fasterxml.jackson.databind.*; -public class TestJdkTypes - extends com.fasterxml.jackson.databind.BaseMapTest +public class TestJdkTypes extends BaseMapTest { static class PrimitivesBean { @@ -48,16 +48,28 @@ public ParamClassBean(String name) { this.name = name; clazz = String.class; } - } + } + static class BooleanBean { + public Boolean wrapper; + public boolean primitive; + + protected Boolean ctor; + + @JsonCreator + public BooleanBean(@JsonProperty("ctor") Boolean foo) { + ctor = foo; + } + } + /* /********************************************************** /* Test methods /********************************************************** */ - - private final ObjectMapper MAPPER = new ObjectMapper(); + private final ObjectMapper mapper = new ObjectMapper(); + /** * Related to issue [JACKSON-155]. */ @@ -65,7 +77,7 @@ public void testFile() throws Exception { // Not portable etc... has to do: File src = new File("/test").getAbsoluteFile(); - File result = MAPPER.readValue("\""+src.getAbsolutePath()+"\"", File.class); + File result = mapper.readValue("\""+src.getAbsolutePath()+"\"", File.class); assertEquals(src.getAbsolutePath(), result.getAbsolutePath()); } @@ -76,41 +88,45 @@ public void testRegexps() throws IOException /* Ok: easiest way is to just serialize first; problem * is the backslash */ - String json = MAPPER.writeValueAsString(exp); - Pattern result = MAPPER.readValue(json, Pattern.class); + String json = mapper.writeValueAsString(exp); + Pattern result = mapper.readValue(json, Pattern.class); assertEquals(exp.pattern(), result.pattern()); } public void testCurrency() throws IOException { Currency usd = Currency.getInstance("USD"); - assertEquals(usd, MAPPER.readValue(quote("USD"), Currency.class)); + assertEquals(usd, new ObjectMapper().readValue(quote("USD"), Currency.class)); } /** * Test for [JACKSON-419] + * + * @since 1.7 */ public void testLocale() throws IOException { - assertEquals(new Locale("en"), MAPPER.readValue(quote("en"), Locale.class)); - assertEquals(new Locale("es", "ES"), MAPPER.readValue(quote("es_ES"), Locale.class)); - assertEquals(new Locale("FI", "fi", "savo"), MAPPER.readValue(quote("fi_FI_savo"), Locale.class)); + assertEquals(new Locale("en"), mapper.readValue(quote("en"), Locale.class)); + assertEquals(new Locale("es", "ES"), mapper.readValue(quote("es_ES"), Locale.class)); + assertEquals(new Locale("FI", "fi", "savo"), mapper.readValue(quote("fi_FI_savo"), Locale.class)); } /** * Test for [JACKSON-420] (add DeserializationConfig.FAIL_ON_NULL_FOR_PRIMITIVES) + * + * @since 1.7 */ public void testNullForPrimitives() throws IOException { // by default, ok to rely on defaults - PrimitivesBean bean = MAPPER.readValue("{\"intValue\":null, \"booleanValue\":null, \"doubleValue\":null}", + PrimitivesBean bean = mapper.readValue("{\"intValue\":null, \"booleanValue\":null, \"doubleValue\":null}", PrimitivesBean.class); assertNotNull(bean); assertEquals(0, bean.intValue); assertEquals(false, bean.booleanValue); assertEquals(0.0, bean.doubleValue); - bean = MAPPER.readValue("{\"byteValue\":null, \"longValue\":null, \"floatValue\":null}", + bean = mapper.readValue("{\"byteValue\":null, \"longValue\":null, \"floatValue\":null}", PrimitivesBean.class); assertNotNull(bean); assertEquals((byte) 0, bean.byteValue); @@ -118,43 +134,43 @@ public void testNullForPrimitives() throws IOException assertEquals(0.0f, bean.floatValue); // but not when enabled - ObjectMapper mapper = new ObjectMapper(); - mapper.configure(DeserializationConfig.Feature.FAIL_ON_NULL_FOR_PRIMITIVES, true); + final ObjectMapper mapper2 = new ObjectMapper(); + mapper2.configure(DeserializationConfig.Feature.FAIL_ON_NULL_FOR_PRIMITIVES, true); // boolean try { - mapper.readValue("{\"booleanValue\":null}", PrimitivesBean.class); + mapper2.readValue("{\"booleanValue\":null}", PrimitivesBean.class); fail("Expected failure for boolean + null"); } catch (JsonMappingException e) { verifyException(e, "Can not map JSON null into type boolean"); } // byte/char/short/int/long try { - mapper.readValue("{\"byteValue\":null}", PrimitivesBean.class); + mapper2.readValue("{\"byteValue\":null}", PrimitivesBean.class); fail("Expected failure for byte + null"); } catch (JsonMappingException e) { verifyException(e, "Can not map JSON null into type byte"); } try { - mapper.readValue("{\"charValue\":null}", PrimitivesBean.class); + mapper2.readValue("{\"charValue\":null}", PrimitivesBean.class); fail("Expected failure for char + null"); } catch (JsonMappingException e) { verifyException(e, "Can not map JSON null into type char"); } try { - mapper.readValue("{\"shortValue\":null}", PrimitivesBean.class); + mapper2.readValue("{\"shortValue\":null}", PrimitivesBean.class); fail("Expected failure for short + null"); } catch (JsonMappingException e) { verifyException(e, "Can not map JSON null into type short"); } try { - mapper.readValue("{\"intValue\":null}", PrimitivesBean.class); + mapper2.readValue("{\"intValue\":null}", PrimitivesBean.class); fail("Expected failure for int + null"); } catch (JsonMappingException e) { verifyException(e, "Can not map JSON null into type int"); } try { - mapper.readValue("{\"longValue\":null}", PrimitivesBean.class); + mapper2.readValue("{\"longValue\":null}", PrimitivesBean.class); fail("Expected failure for long + null"); } catch (JsonMappingException e) { verifyException(e, "Can not map JSON null into type long"); @@ -162,13 +178,13 @@ public void testNullForPrimitives() throws IOException // float/double try { - mapper.readValue("{\"floatValue\":null}", PrimitivesBean.class); + mapper2.readValue("{\"floatValue\":null}", PrimitivesBean.class); fail("Expected failure for float + null"); } catch (JsonMappingException e) { verifyException(e, "Can not map JSON null into type float"); } try { - mapper.readValue("{\"doubleValue\":null}", PrimitivesBean.class); + mapper2.readValue("{\"doubleValue\":null}", PrimitivesBean.class); fail("Expected failure for double + null"); } catch (JsonMappingException e) { verifyException(e, "Can not map JSON null into type double"); @@ -180,7 +196,7 @@ public void testNullForPrimitives() throws IOException */ public void testCharSequence() throws IOException { - CharSequence cs = MAPPER.readValue("\"abc\"", CharSequence.class); + CharSequence cs = mapper.readValue("\"abc\"", CharSequence.class); assertEquals(String.class, cs.getClass()); assertEquals("abc", cs.toString()); } @@ -188,38 +204,39 @@ public void testCharSequence() throws IOException // [JACKSON-484] public void testInetAddress() throws IOException { - InetAddress address = MAPPER.readValue(quote("127.0.0.1"), InetAddress.class); + InetAddress address = mapper.readValue(quote("127.0.0.1"), InetAddress.class); assertEquals("127.0.0.1", address.getHostAddress()); // should we try resolving host names? That requires connectivity... final String HOST = "www.ning.com"; - address = MAPPER.readValue(quote(HOST), InetAddress.class); + address = mapper.readValue(quote(HOST), InetAddress.class); assertEquals(HOST, address.getHostName()); } // [JACKSON-597] public void testClass() throws IOException { - assertSame(String.class, MAPPER.readValue(quote("java.lang.String"), Class.class)); + ObjectMapper mapper = new ObjectMapper(); + assertSame(String.class, mapper.readValue(quote("java.lang.String"), Class.class)); // then primitive types - assertSame(Boolean.TYPE, MAPPER.readValue(quote("boolean"), Class.class)); - assertSame(Byte.TYPE, MAPPER.readValue(quote("byte"), Class.class)); - assertSame(Short.TYPE, MAPPER.readValue(quote("short"), Class.class)); - assertSame(Character.TYPE, MAPPER.readValue(quote("char"), Class.class)); - assertSame(Integer.TYPE, MAPPER.readValue(quote("int"), Class.class)); - assertSame(Long.TYPE, MAPPER.readValue(quote("long"), Class.class)); - assertSame(Float.TYPE, MAPPER.readValue(quote("float"), Class.class)); - assertSame(Double.TYPE, MAPPER.readValue(quote("double"), Class.class)); - assertSame(Void.TYPE, MAPPER.readValue(quote("void"), Class.class)); + assertSame(Boolean.TYPE, mapper.readValue(quote("boolean"), Class.class)); + assertSame(Byte.TYPE, mapper.readValue(quote("byte"), Class.class)); + assertSame(Short.TYPE, mapper.readValue(quote("short"), Class.class)); + assertSame(Character.TYPE, mapper.readValue(quote("char"), Class.class)); + assertSame(Integer.TYPE, mapper.readValue(quote("int"), Class.class)); + assertSame(Long.TYPE, mapper.readValue(quote("long"), Class.class)); + assertSame(Float.TYPE, mapper.readValue(quote("float"), Class.class)); + assertSame(Double.TYPE, mapper.readValue(quote("double"), Class.class)); + assertSame(Void.TYPE, mapper.readValue(quote("void"), Class.class)); } // [JACKSON-605] public void testClassWithParams() throws IOException { - String json = MAPPER.writeValueAsString(new ParamClassBean("Foobar")); + String json = mapper.writeValueAsString(new ParamClassBean("Foobar")); - ParamClassBean result = MAPPER.readValue(json, ParamClassBean.class); + ParamClassBean result = mapper.readValue(json, ParamClassBean.class); assertEquals("Foobar", result.name); assertSame(String.class, result.clazz); } @@ -230,59 +247,72 @@ public void testEmptyStringForWrappers() throws IOException WrappersBean bean; // by default, ok to rely on defaults - bean = MAPPER.readValue("{\"booleanValue\":\"\"}", WrappersBean.class); + bean = mapper.readValue("{\"booleanValue\":\"\"}", WrappersBean.class); assertNull(bean.booleanValue); - bean = MAPPER.readValue("{\"byteValue\":\"\"}", WrappersBean.class); + bean = mapper.readValue("{\"byteValue\":\"\"}", WrappersBean.class); assertNull(bean.byteValue); // char/Character is different... not sure if this should work or not: - bean = MAPPER.readValue("{\"charValue\":\"\"}", WrappersBean.class); + bean = mapper.readValue("{\"charValue\":\"\"}", WrappersBean.class); assertNull(bean.charValue); - bean = MAPPER.readValue("{\"shortValue\":\"\"}", WrappersBean.class); + bean = mapper.readValue("{\"shortValue\":\"\"}", WrappersBean.class); assertNull(bean.shortValue); - bean = MAPPER.readValue("{\"intValue\":\"\"}", WrappersBean.class); + bean = mapper.readValue("{\"intValue\":\"\"}", WrappersBean.class); assertNull(bean.intValue); - bean = MAPPER.readValue("{\"longValue\":\"\"}", WrappersBean.class); + bean = mapper.readValue("{\"longValue\":\"\"}", WrappersBean.class); assertNull(bean.longValue); - bean = MAPPER.readValue("{\"floatValue\":\"\"}", WrappersBean.class); + bean = mapper.readValue("{\"floatValue\":\"\"}", WrappersBean.class); assertNull(bean.floatValue); - bean = MAPPER.readValue("{\"doubleValue\":\"\"}", WrappersBean.class); + bean = mapper.readValue("{\"doubleValue\":\"\"}", WrappersBean.class); assertNull(bean.doubleValue); } // for [JACKSON-616] + // @since 1.9 public void testEmptyStringForPrimitives() throws IOException { PrimitivesBean bean; - bean = MAPPER.readValue("{\"booleanValue\":\"\"}", PrimitivesBean.class); + bean = mapper.readValue("{\"booleanValue\":\"\"}", PrimitivesBean.class); assertFalse(bean.booleanValue); - bean = MAPPER.readValue("{\"byteValue\":\"\"}", PrimitivesBean.class); + bean = mapper.readValue("{\"byteValue\":\"\"}", PrimitivesBean.class); assertEquals((byte) 0, bean.byteValue); - bean = MAPPER.readValue("{\"charValue\":\"\"}", PrimitivesBean.class); + bean = mapper.readValue("{\"charValue\":\"\"}", PrimitivesBean.class); assertEquals((char) 0, bean.charValue); - bean = MAPPER.readValue("{\"shortValue\":\"\"}", PrimitivesBean.class); + bean = mapper.readValue("{\"shortValue\":\"\"}", PrimitivesBean.class); assertEquals((short) 0, bean.shortValue); - bean = MAPPER.readValue("{\"intValue\":\"\"}", PrimitivesBean.class); + bean = mapper.readValue("{\"intValue\":\"\"}", PrimitivesBean.class); assertEquals(0, bean.intValue); - bean = MAPPER.readValue("{\"longValue\":\"\"}", PrimitivesBean.class); + bean = mapper.readValue("{\"longValue\":\"\"}", PrimitivesBean.class); assertEquals(0L, bean.longValue); - bean = MAPPER.readValue("{\"floatValue\":\"\"}", PrimitivesBean.class); + bean = mapper.readValue("{\"floatValue\":\"\"}", PrimitivesBean.class); assertEquals(0.0f, bean.floatValue); - bean = MAPPER.readValue("{\"doubleValue\":\"\"}", PrimitivesBean.class); + bean = mapper.readValue("{\"doubleValue\":\"\"}", PrimitivesBean.class); assertEquals(0.0, bean.doubleValue); } // for [JACKSON-652] + // @since 1.9 public void testUntypedWithJsonArrays() throws Exception { // by default we get: - Object ob = MAPPER.readValue("[1]", Object.class); + Object ob = mapper.readValue("[1]", Object.class); assertTrue(ob instanceof List); // but can change to produce Object[]: - MAPPER.configure(DeserializationConfig.Feature.USE_JAVA_ARRAY_FOR_JSON_ARRAY, true); - ob = MAPPER.readValue("[1]", Object.class); + mapper.configure(DeserializationConfig.Feature.USE_JAVA_ARRAY_FOR_JSON_ARRAY, true); + ob = mapper.readValue("[1]", Object.class); assertEquals(Object[].class, ob.getClass()); } + + // Test for verifying that Long values are coerced to boolean correctly as well + public void testLongToBoolean() throws Exception + { + long value = 1L + Integer.MAX_VALUE; + BooleanBean b = mapper.readValue("{\"primitive\" : "+value+", \"wrapper\":"+value+", \"ctor\":"+value+"}", + BooleanBean.class); + assertEquals(Boolean.TRUE, b.wrapper); + assertTrue(b.primitive); + assertEquals(Boolean.TRUE, b.ctor); + } }