aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTatu Saloranta <tatu.saloranta@iki.fi>2020-05-05 17:01:54 -0700
committerTatu Saloranta <tatu.saloranta@iki.fi>2020-05-05 17:01:54 -0700
commita39892cc317730f346b94b281f1c3cdd9f32b218 (patch)
tree0f61292e724bcde7bbd5ff640742f54919e84865
parentfa3ef3e680320813cb63360b7208f438926f8612 (diff)
downloadjackson-databind-a39892cc317730f346b94b281f1c3cdd9f32b218.tar.gz
Fix #2683
-rw-r--r--release-notes/VERSION-2.x2
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java44
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/deser/impl/FailingDeserializer.java10
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/deser/impl/UnsupportedTypeDeserializer.java39
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerFactory.java28
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/ser/impl/FailingSerializer.java20
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/ser/impl/UnsupportedTypeSerializer.java37
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/util/ClassUtil.java7
-rw-r--r--src/test/java/com/fasterxml/jackson/databind/deser/jdk/DateJava8FallbacksTest.java43
-rw-r--r--src/test/java/com/fasterxml/jackson/databind/jsontype/TestWithGenerics.java2
10 files changed, 200 insertions, 32 deletions
diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x
index 43bc0894e..f25401e09 100644
--- a/release-notes/VERSION-2.x
+++ b/release-notes/VERSION-2.x
@@ -8,6 +8,8 @@ Project: jackson-databind
#792: Deserialization Not Working Right with Generic Types and Builders
(reported by Mike G; fix contributed by Ville K)
+#2683: Explicitly fail (de)serialization of `java.time.*` types in absence of
+ registered custom (de)serializers
#2707: Improve description included in by `DeserializationContext.handleUnexpectedToken()`
2.11.1 (not yet released)
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java
index f9eee8587..8b93b7fd1 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java
@@ -93,7 +93,7 @@ public class BeanDeserializerFactory
throws JsonMappingException
{
final DeserializationConfig config = ctxt.getConfig();
- // We may also have custom overrides:
+ // First: we may also have custom overrides:
JsonDeserializer<?> deser = _findCustomBeanDeserializer(type, config, beanDesc);
if (deser != null) {
// [databind#2392]
@@ -104,10 +104,8 @@ public class BeanDeserializerFactory
}
return (JsonDeserializer<Object>) deser;
}
- /* One more thing to check: do we have an exception type
- * (Throwable or its sub-classes)? If so, need slightly
- * different handling.
- */
+ // One more thing to check: do we have an exception type (Throwable or its
+ // sub-classes)? If so, need slightly different handling.
if (type.isThrowable()) {
return buildThrowableDeserializer(ctxt, type, beanDesc);
}
@@ -139,6 +137,14 @@ public class BeanDeserializerFactory
}
// For checks like [databind#1599]
_validateSubType(ctxt, type, beanDesc);
+
+ // 05-May-2020, tatu: [databind#2683] Let's actually pre-emptively catch
+ // certain types (for now, java.time.*) to give better error messages
+ deser = _findUnsupportedTypeDeserializer(ctxt, type, beanDesc);
+ if (deser != null) {
+ return (JsonDeserializer<Object>)deser;
+ }
+
// Use generic bean introspection to build deserializer
return buildBeanDeserializer(ctxt, type, beanDesc);
}
@@ -181,7 +187,30 @@ public class BeanDeserializerFactory
}
return deser;
}
-
+
+ /**
+ * Helper method called to see if given type, otherwise to be taken as POJO type,
+ * is "known but not supported" JDK type, and if so, return alternate handler
+ * (deserializer).
+ * Initially added to support more meaningful error messages when "Java 8 date/time"
+ * support module not registered.
+ *
+ * @since 2.12
+ */
+ protected JsonDeserializer<Object> _findUnsupportedTypeDeserializer(DeserializationContext ctxt,
+ JavaType type, BeanDescription beanDesc)
+ throws JsonMappingException
+ {
+ if (ClassUtil.isJava8TimeClass(type.getRawClass())) {
+ // 05-May-2020, tatu: Should we check for possible Shape override to "POJO"?
+ // (to let users force 'serialize-as-POJO'?
+ return new UnsupportedTypeDeserializer(type,
+"Java 8 date/time type "+ClassUtil.getTypeDescription(type)
++" not supported by default: please register module `jackson-datatype-jsr310` to add handling");
+ }
+ return null;
+ }
+
protected JavaType materializeAbstractType(DeserializationContext ctxt,
JavaType type, BeanDescription beanDesc)
throws JsonMappingException
@@ -434,8 +463,7 @@ public class BeanDeserializerFactory
/*
/**********************************************************
- /* Helper methods for Bean deserializer construction,
- /* overridable by sub-classes
+ /* Helper methods for Bean deserializer construction
/**********************************************************
*/
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/FailingDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/FailingDeserializer.java
index 9df6742bd..ba9b55299 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/FailingDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/FailingDeserializer.java
@@ -4,12 +4,11 @@ import java.io.IOException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
/**
* Special bogus "serializer" that will throw
- * {@link JsonMappingException} if an attempt is made to deserialize
+ * {@link com.fasterxml.jackson.databind.exc.MismatchedInputException} if an attempt is made to deserialize
* a value. This is used as placeholder to avoid NPEs for uninitialized
* structured serializers or handlers.
*/
@@ -20,7 +19,12 @@ public class FailingDeserializer extends StdDeserializer<Object>
protected final String _message;
public FailingDeserializer(String m) {
- super(Object.class);
+ this(Object.class, m);
+ }
+
+ // @since 2.12
+ public FailingDeserializer(Class<?> rawType, String m) {
+ super(rawType);
_message = m;
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/UnsupportedTypeDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/UnsupportedTypeDeserializer.java
new file mode 100644
index 000000000..a7a39148b
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/UnsupportedTypeDeserializer.java
@@ -0,0 +1,39 @@
+package com.fasterxml.jackson.databind.deser.impl;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+
+/**
+ * Special bogus "serializer" that will throw
+ * {@link com.fasterxml.jackson.databind.exc.MismatchedInputException}
+ * if an attempt is made to deserialize a value.
+ * This is used for "known unknown" types: types that we can recognize
+ * but can not support easily (or support known to be added via extension
+ * module).
+ *
+ * @since 2.12
+ */
+public class UnsupportedTypeDeserializer extends StdDeserializer<Object>
+{
+ private static final long serialVersionUID = 1L;
+
+ protected final JavaType _type;
+
+ protected final String _message;
+
+ public UnsupportedTypeDeserializer(JavaType t, String m) {
+ super(t);
+ _type = t;
+ _message = m;
+ }
+
+ @Override
+ public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
+ ctxt.reportBadDefinition(_type, _message);
+ return null;
+ }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerFactory.java b/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerFactory.java
index 79c9a96f0..66e682e5f 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerFactory.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerFactory.java
@@ -9,6 +9,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.cfg.SerializerFactoryConfig;
+import com.fasterxml.jackson.databind.deser.impl.UnsupportedTypeDeserializer;
import com.fasterxml.jackson.databind.introspect.*;
import com.fasterxml.jackson.databind.jsontype.NamedType;
import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
@@ -16,6 +17,7 @@ import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.ser.impl.FilteredBeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.impl.ObjectIdWriter;
import com.fasterxml.jackson.databind.ser.impl.PropertyBasedObjectIdGenerator;
+import com.fasterxml.jackson.databind.ser.impl.UnsupportedTypeSerializer;
import com.fasterxml.jackson.databind.ser.std.MapSerializer;
import com.fasterxml.jackson.databind.ser.std.StdDelegatingSerializer;
import com.fasterxml.jackson.databind.type.ReferenceType;
@@ -376,6 +378,12 @@ public class BeanSerializerFactory
return prov.getUnknownTypeSerializer(Object.class);
// throw new IllegalArgumentException("Cannot create bean serializer for Object.class");
}
+
+ JsonSerializer<?> ser = _findUnsupportedTypeSerializer(prov, type, beanDesc);
+ if (ser != null) {
+ return (JsonSerializer<Object>) ser;
+ }
+
final SerializationConfig config = prov.getConfig();
BeanSerializerBuilder builder = constructBeanSerializerBuilder(beanDesc);
builder.setConfig(config);
@@ -447,9 +455,8 @@ public class BeanSerializerFactory
}
}
- JsonSerializer<Object> ser = null;
try {
- ser = (JsonSerializer<Object>) builder.build();
+ ser = builder.build();
} catch (RuntimeException e) {
return prov.reportBadTypeDefinition(beanDesc, "Failed to construct BeanSerializer for %s: (%s) %s",
beanDesc.getType(), e.getClass().getName(), e.getMessage());
@@ -467,7 +474,7 @@ public class BeanSerializerFactory
}
}
}
- return ser;
+ return (JsonSerializer<Object>) ser;
}
protected ObjectIdWriter constructObjectIdHandler(SerializerProvider prov,
@@ -814,4 +821,19 @@ public class BeanSerializerFactory
return pb.buildWriter(prov, propDef, type, annotatedSerializer,
typeSer, contentTypeSer, accessor, staticTyping);
}
+
+ protected JsonSerializer<?> _findUnsupportedTypeSerializer(SerializerProvider ctxt,
+ JavaType type, BeanDescription beanDesc)
+ throws JsonMappingException
+ {
+ if (ClassUtil.isJava8TimeClass(type.getRawClass())) {
+ // 05-May-2020, tatu: Should we check for possible Shape override to "POJO"?
+ // (to let users force 'serialize-as-POJO'?
+ return new UnsupportedTypeSerializer(type,
+"Java 8 date/time type "+ClassUtil.getTypeDescription(type)
++" not supported by default: please register module `jackson-datatype-jsr310` to add handling");
+ }
+ return null;
+
+ }
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/FailingSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/FailingSerializer.java
index 6021fa89e..e047c6010 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/FailingSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/FailingSerializer.java
@@ -1,14 +1,11 @@
package com.fasterxml.jackson.databind.ser.impl;
import java.io.IOException;
-import java.lang.reflect.Type;
import com.fasterxml.jackson.core.*;
-import com.fasterxml.jackson.databind.JavaType;
+
import com.fasterxml.jackson.databind.JsonMappingException;
-import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.SerializerProvider;
-import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
/**
@@ -30,19 +27,8 @@ public class FailingSerializer
}
@Override
- public void serialize(Object value, JsonGenerator g, SerializerProvider provider) throws IOException
- {
- provider.reportMappingProblem(_msg);
- }
-
- @Override
- public JsonNode getSchema(SerializerProvider provider, Type typeHint) throws JsonMappingException {
- return null;
- }
-
- @Override
- public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+ public void serialize(Object value, JsonGenerator g, SerializerProvider ctxt) throws IOException
{
- ;
+ ctxt.reportMappingProblem(_msg);
}
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnsupportedTypeSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnsupportedTypeSerializer.java
new file mode 100644
index 000000000..b849ee3d2
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnsupportedTypeSerializer.java
@@ -0,0 +1,37 @@
+package com.fasterxml.jackson.databind.ser.impl;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+
+/**
+ * Special bogus "serializer" that will throw
+ * {@link com.fasterxml.jackson.databind.exc.InvalidDefinitionException} if its {@link #serialize}
+ * gets invoked. Most commonly registered as handler for unknown types,
+ * as well as for catching unintended usage (like trying to use null
+ * as Map/Object key).
+ */
+public class UnsupportedTypeSerializer
+ extends StdSerializer<Object>
+{
+ private static final long serialVersionUID = 1L;
+
+ protected final JavaType _type;
+
+ protected final String _message;
+
+ public UnsupportedTypeSerializer(JavaType t, String msg) {
+ super(Object.class);
+ _type = t;
+ _message = msg;
+ }
+
+ @Override
+ public void serialize(Object value, JsonGenerator g, SerializerProvider ctxt) throws IOException {
+ ctxt.reportBadDefinition(_type, _message);
+ }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/ClassUtil.java b/src/main/java/com/fasterxml/jackson/databind/util/ClassUtil.java
index 2a984dc90..e3fb8caad 100644
--- a/src/main/java/com/fasterxml/jackson/databind/util/ClassUtil.java
+++ b/src/main/java/com/fasterxml/jackson/databind/util/ClassUtil.java
@@ -1084,6 +1084,13 @@ public final class ClassUtil
return rawType.getName().startsWith("java.");
}
+ /**
+ * @since 2.12
+ */
+ public static boolean isJava8TimeClass(Class<?> rawType) {
+ return rawType.getName().startsWith("java.time.");
+ }
+
/*
/**********************************************************
/* Access to various Class definition aspects; possibly
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/DateJava8FallbacksTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/DateJava8FallbacksTest.java
new file mode 100644
index 000000000..41d9fa4f0
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/DateJava8FallbacksTest.java
@@ -0,0 +1,43 @@
+package com.fasterxml.jackson.databind.deser.jdk;
+
+import java.time.Instant;
+import java.time.OffsetDateTime;
+import java.time.ZoneOffset;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
+
+// [databind#2683]: add fallback handling for Java 8 date/time types, to
+// prevent accidental serialzization as POJOs, as well as give more information
+// on deserialization attempts
+public class DateJava8FallbacksTest extends BaseMapTest
+{
+ private final ObjectMapper MAPPER = newJsonMapper();
+
+ private final OffsetDateTime DATETIME_EPOCH = OffsetDateTime.ofInstant(Instant.ofEpochSecond(0L),
+ ZoneOffset.of("Z"));
+
+ // Test to prevent serialization as POJO, without Java 8 date/time module:
+ public void testPreventSerialization() throws Exception
+ {
+ try {
+ String json = MAPPER.writerWithDefaultPrettyPrinter()
+ .writeValueAsString(DATETIME_EPOCH);
+ fail("Should not pass, wrote out as\n: "+json);
+ } catch (InvalidDefinitionException e) {
+ verifyException(e, "Java 8 date/time type `java.time.OffsetDateTime` not supported by default");
+ verifyException(e, "please register module `jackson-datatype-jsr310`");
+ }
+ }
+
+ public void testBetterDeserializationError() throws Exception
+ {
+ try {
+ OffsetDateTime result = MAPPER.readValue(" 0 ", OffsetDateTime.class);
+ fail("Not expecting to pass, resulted in: "+result);
+ } catch (InvalidDefinitionException e) {
+ verifyException(e, "Java 8 date/time type `java.time.OffsetDateTime` not supported by default");
+ verifyException(e, "please register module `jackson-datatype-jsr310`");
+ }
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestWithGenerics.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestWithGenerics.java
index 15d95f82d..8a066f6d2 100644
--- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestWithGenerics.java
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestWithGenerics.java
@@ -126,7 +126,7 @@ public class TestWithGenerics extends BaseMapTest
otherAnimal = a2;
}
}
-
+
/*
/**********************************************************
/* Unit tests