Skip to content

Commit

Permalink
Fix #4082: add check for attempts to ser/deser Java 8 optionals witho…
Browse files Browse the repository at this point in the history
…ut module
  • Loading branch information
cowtowncoder committed Aug 20, 2023
1 parent 45e6fa6 commit 5d504ca
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 0 deletions.
2 changes: 2 additions & 0 deletions release-notes/VERSION-2.x
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ Project: jackson-databind
#4078: `java.desktop` module is no longer optional
(reported by Andreas Z)
(fix contributed by Joo-Hyuk K)
#4082: `ClassUtil` fails with `java.lang.reflect.InaccessibleObjectException`
trying to setAccessible on `OptionalInt` with JDK 17+

2.15.3 (not yet released)

Expand Down
17 changes: 17 additions & 0 deletions src/main/java/com/fasterxml/jackson/databind/util/BeanUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,9 @@ public static String checkUnsupportedType(JavaType type) {
} else if (isJodaTimeClass(className)) {
typeName = "Joda date/time";
moduleName = "com.fasterxml.jackson.datatype:jackson-datatype-joda";
} else if (isJava8OptionalClass(className)) {
typeName = "Java 8 optional";
moduleName = "com.fasterxml.jackson.datatype:jackson-datatype-jdk8";
} else {
return null;
}
Expand All @@ -323,17 +326,31 @@ public static boolean isJava8TimeClass(Class<?> rawType) {
return isJava8TimeClass(rawType.getName());
}

// @since 2.12
private static boolean isJava8TimeClass(String className) {
return className.startsWith("java.time.");
}

/**
* @since 2.16
*/
public static boolean isJava8OptionalClass(Class<?> rawType) {
return isJava8OptionalClass(rawType.getName());
}

// @since 2.16
private static boolean isJava8OptionalClass(String className) {
return className.startsWith("java.util.Optional");
}

/**
* @since 2.12
*/
public static boolean isJodaTimeClass(Class<?> rawType) {
return isJodaTimeClass(rawType.getName());
}

// @since 2.12
private static boolean isJodaTimeClass(String className) {
return className.startsWith("org.joda.time.");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package com.fasterxml.jackson.databind.interop;

import java.util.Optional;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.OptionalLong;

import com.fasterxml.jackson.core.*;

import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
import com.fasterxml.jackson.databind.util.TokenBuffer;

// [databind#4082]: add fallback handling for Java 8 Optional types, to
// prevent accidental serialization as POJOs, as well as give more information
// on deserialization attempts
//
// @since 2.16
public class OptionalJava8Fallbacks4082Test extends BaseMapTest
{
private final ObjectMapper MAPPER = newJsonMapper();

// Test to prevent serialization as POJO, without Java 8 date/time module:
public void testPreventSerialization() throws Exception {
_testPreventSerialization(Optional.empty());
_testPreventSerialization(OptionalInt.of(13));
_testPreventSerialization(OptionalLong.of(-1L));
_testPreventSerialization(OptionalDouble.of(0.5));
}

private void _testPreventSerialization(Object value) throws Exception
{
try {
String json = MAPPER.writeValueAsString(value);
fail("Should not pass, wrote out as\n: "+json);
} catch (InvalidDefinitionException e) {
verifyException(e, "Java 8 optional type `"+value.getClass().getName()
+"` not supported by default");
verifyException(e, "add Module \"com.fasterxml.jackson.datatype:jackson-datatype-jdk8\"");
}
}

public void testBetterDeserializationError() throws Exception
{
_testBetterDeserializationError(Optional.class);
_testBetterDeserializationError(OptionalInt.class);
_testBetterDeserializationError(OptionalLong.class);
_testBetterDeserializationError(OptionalDouble.class);
}

private void _testBetterDeserializationError(Class<?> target) throws Exception
{
try {
Object result = MAPPER.readValue(" 0 ", target);
fail("Not expecting to pass, resulted in: "+result);
} catch (InvalidDefinitionException e) {
verifyException(e, "Java 8 optional type `"+target.getName()+"` not supported by default");
verifyException(e, "add Module \"com.fasterxml.jackson.datatype:jackson-datatype-jdk8\"");
}
}

// But, [databind#3091], allow deser from JsonToken.VALUE_EMBEDDED_OBJECT
public void testAllowAsEmbedded() throws Exception
{
Optional<Object> optValue = Optional.empty();
try (TokenBuffer tb = new TokenBuffer((ObjectCodec) null, false)) {
tb.writeEmbeddedObject(optValue);

try (JsonParser p = tb.asParser()) {
Optional<?> result = MAPPER.readValue(p, Optional.class);
assertSame(optValue, result);
}
}

// but also try deser into an array
try (TokenBuffer tb = new TokenBuffer((ObjectCodec) null, false)) {
tb.writeStartArray();
tb.writeEmbeddedObject(optValue);
tb.writeEndArray();

try (JsonParser p = tb.asParser()) {
Object[] result = MAPPER.readValue(p, Object[].class);
assertNotNull(result);
assertEquals(1, result.length);
assertSame(optValue, result[0]);
}
}
}
}

0 comments on commit 5d504ca

Please sign in to comment.