This is an automated email from the ASF dual-hosted git repository. struberg pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/johnzon.git
The following commit(s) were added to refs/heads/master by this push: new d4923a7f add native support for Optional in Mapper d4923a7f is described below commit d4923a7fe78cdfd8129f19d0bac2b737e6efeffc Author: Mark Struberg <strub...@apache.org> AuthorDate: Wed Oct 11 12:22:14 2023 +0200 add native support for Optional in Mapper --- .../org/apache/johnzon/jsonb/JsonbTypesTest.java | 62 +++++++++++++++++++++- .../apache/johnzon/mapper/MappingParserImpl.java | 57 +++++++++++--------- 2 files changed, 93 insertions(+), 26 deletions(-) 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 2c5987bc..04d43705 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 java.util.concurrent.TimeUnit; 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 @@ public class JsonbTypesTest { 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 @@ public class JsonbTypesTest { 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 de9c1d3e..61dd5c95 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.ParameterizedType; 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 @@ public class MappingParserImpl implements MappingParser { 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 @@ public class MappingParserImpl implements MappingParser { 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 @@ public class MappingParserImpl implements MappingParser { 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 @@ public class MappingParserImpl implements MappingParser { 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 @@ public class MappingParserImpl implements MappingParser { } 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.");