add native support for Optional in Mapper
diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JsonbTypesTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JsonbTypesTest.java
index 2c5987b..04d4370 100644
--- a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JsonbTypesTest.java
+++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JsonbTypesTest.java
@@ -19,6 +19,7 @@
package org.apache.johnzon.jsonb;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import java.io.StringReader;
import java.math.BigDecimal;
@@ -54,6 +55,8 @@
import jakarta.json.bind.Jsonb;
import jakarta.json.bind.JsonbConfig;
+import jakarta.json.bind.annotation.JsonbCreator;
+import jakarta.json.bind.annotation.JsonbProperty;
import jakarta.json.bind.spi.JsonbProvider;
import org.apache.cxf.common.util.StringUtils;
@@ -131,7 +134,37 @@
readAndWriteWithDateFormat(DateTimeFormatter.ofPattern("yyyyMMdd+HHmmssZ"), "yyyyMMdd+HHmmssZ");
readAndWriteWithDateFormat(DateTimeFormatter.ofPattern("yyyy-MM-dd"), "yyyy-MM-dd");
}
-
+
+ @Test
+ public void testOptionalViaJsonbCreatorFullJson() {
+ final Jsonb jsonb = newJsonb();
+ final String json = "{\"intOptional\":4711,\"stringOptional\":\"testVal\"}";
+ final OptionalTypes optionalTypes = jsonb.fromJson(json, OptionalTypes.class);
+ assertNotNull(optionalTypes);
+ assertEquals("testVal", optionalTypes.getOptionalString());
+ assertEquals(4711, optionalTypes.getOptionalInt());
+ }
+
+ @Test
+ public void testOptionalViaJsonbCreatorEmptyJson() {
+ final Jsonb jsonb = newJsonb();
+ final String json = "{ }";
+ final OptionalTypes optionalTypes = jsonb.fromJson(json, OptionalTypes.class);
+ assertNotNull(optionalTypes);
+ assertEquals(OptionalTypes.EMPTY, optionalTypes.getOptionalString());
+ assertEquals(-1, optionalTypes.getOptionalInt());
+ }
+
+ @Test
+ public void testOptionalViaJsonbCreatorPartialJson() {
+ final Jsonb jsonb = newJsonb();
+ final String json = "{\"intOptional\":4711}";
+ final OptionalTypes optionalTypes = jsonb.fromJson(json, OptionalTypes.class);
+ assertNotNull(optionalTypes);
+ assertEquals(OptionalTypes.EMPTY, optionalTypes.getOptionalString());
+ assertEquals(4711, optionalTypes.getOptionalInt());
+ }
+
private void readAndWriteWithDateFormat(DateTimeFormatter dateTimeFormatter, String dateFormat) throws Exception {
final LocalDate localDate = LocalDate.of(2015, 1, 1);
final LocalDateTime localDateTime = LocalDateTime.of(2015, 1, 1, 1, 1);
@@ -520,4 +553,31 @@
date, calendar, gregorianCalendar, instant, localDateTime, localDate, offsetDateTime);
}
}
+
+ public static class OptionalTypes {
+ public final static String EMPTY = " ";
+ private final String optionalString;
+ private final int optionalInt;
+
+ public OptionalTypes(String optionalString, int optionalInt) {
+ this.optionalString = optionalString;
+ this.optionalInt = optionalInt;
+ }
+
+ @JsonbCreator
+ public static OptionalTypes init(@JsonbProperty("stringOptional") Optional<String> stringOptional,
+ @JsonbProperty("intOptional") OptionalInt intOptional) {
+ return new OptionalTypes(
+ stringOptional.orElse(EMPTY),
+ intOptional.orElse(-1));
+ }
+
+ public String getOptionalString() {
+ return optionalString;
+ }
+
+ public int getOptionalInt() {
+ return optionalInt;
+ }
+ }
}
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParserImpl.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParserImpl.java
index de9c1d3..61dd5c9 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParserImpl.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParserImpl.java
@@ -42,30 +42,7 @@
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Base64;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Deque;
-import java.util.EnumMap;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.NavigableMap;
-import java.util.NavigableSet;
-import java.util.PriorityQueue;
-import java.util.Queue;
-import java.util.Set;
-import java.util.SortedMap;
-import java.util.SortedSet;
-import java.util.TreeMap;
-import java.util.TreeSet;
+import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@@ -621,6 +598,19 @@
final Type type, final Adapter itemConverter, final JsonPointerTracker jsonPointer,
final Type rootType) {
if (jsonValue == null) {
+ if (OptionalInt.class == type) {
+ return OptionalInt.empty();
+ }
+ if (OptionalDouble.class == type) {
+ return OptionalDouble.empty();
+ }
+ if (OptionalLong.class == type) {
+ return OptionalLong.empty();
+ }
+ if (type instanceof ParameterizedType && Optional.class == ((ParameterizedType)type).getRawType()) {
+ return Optional.empty();
+ }
+
return null;
}
@@ -693,6 +683,9 @@
if (type == Long.class || type == long.class) {
return number.longValueExact();
}
+ if (type == OptionalLong.class) {
+ return OptionalLong.of(number.longValueExact());
+ }
if (type == Float.class || type == float.class) {
return (float) number.doubleValue();
@@ -701,6 +694,9 @@
if (type == Double.class || type == double.class) {
return number.doubleValue();
}
+ if (type == OptionalDouble.class) {
+ return OptionalDouble.of(number.doubleValue());
+ }
if (type == BigInteger.class) {
return number.bigIntegerValue();
@@ -713,6 +709,9 @@
if (type == Integer.class || type == int.class) {
return number.intValueExact();
}
+ if (type == OptionalInt.class) {
+ return OptionalInt.of(number.intValueExact());
+ }
if (type == Short.class || type == short.class) {
final int intValue = number.intValue();
@@ -1167,7 +1166,15 @@
}
if (converter == null) {
if (ParameterizedType.class.isInstance(aClass)) {
- return convertTo(ParameterizedType.class.cast(aClass).getRawType(), text);
+ ParameterizedType parameterizedType = (ParameterizedType) aClass;
+ final Type rawType = parameterizedType.getRawType();
+ final Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
+ if (Optional.class == rawType && actualTypeArguments.length == 1) {
+ // convert the type parameter
+ final Type actualType = actualTypeArguments[0];
+ return Optional.of(convertTo(actualType, text));
+ }
+ return convertTo(rawType, text);
}
throw new MapperException("Missing a Converter for type " + aClass + " to convert the JSON String '" +
text + "' . Please register a custom converter for it.");