Record optional parameters (#543)
Signed-off-by: David Kral <david.k.kral@oracle.com>
diff --git a/src/main/java/org/eclipse/yasson/internal/ClassMultiReleaseExtension.java b/src/main/java/org/eclipse/yasson/internal/ClassMultiReleaseExtension.java
index c7adaa1..1f805cf 100644
--- a/src/main/java/org/eclipse/yasson/internal/ClassMultiReleaseExtension.java
+++ b/src/main/java/org/eclipse/yasson/internal/ClassMultiReleaseExtension.java
@@ -36,6 +36,16 @@
return true;
}
+ /**
+ * Whether the class is Record or not.
+ *
+ * @param clazz class to check
+ * @return is record
+ */
+ public static boolean isRecord(Class<?> clazz) {
+ return false;
+ }
+
static boolean isSpecialAccessorMethod(Method method, Map<String, Property> classProperties) {
return false;
}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/ObjectDeserializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/ObjectDeserializer.java
index 38c7ddf..07fcc25 100644
--- a/src/main/java/org/eclipse/yasson/internal/serializer/ObjectDeserializer.java
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/ObjectDeserializer.java
@@ -15,9 +15,16 @@
import java.lang.reflect.Constructor;
import java.lang.reflect.Type;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
+import java.util.OptionalDouble;
+import java.util.OptionalInt;
+import java.util.OptionalLong;
+import java.util.function.Supplier;
import javax.json.bind.JsonbException;
import javax.json.bind.serializer.JsonbDeserializer;
@@ -42,6 +49,28 @@
*/
class ObjectDeserializer<T> extends AbstractContainerDeserializer<T> {
+ private static final Map<Class<?>, Supplier<Object>> DEFAULT_CREATOR_VALUES;
+ private static final Supplier<Object> NULL_PROVIDER = () -> null;
+
+ static {
+ Map<Class<?>, Supplier<Object>> tmpValuesMap = new HashMap<>();
+
+ tmpValuesMap.put(byte.class, () -> (byte) 0);
+ tmpValuesMap.put(short.class, () -> (short) 0);
+ tmpValuesMap.put(int.class, () -> 0);
+ tmpValuesMap.put(long.class, () -> 0L);
+ tmpValuesMap.put(float.class, () -> 0.0F);
+ tmpValuesMap.put(double.class, () -> 0.0);
+ tmpValuesMap.put(char.class, () -> '\u0000');
+ tmpValuesMap.put(boolean.class, () -> false);
+ tmpValuesMap.put(Optional.class, Optional::empty);
+ tmpValuesMap.put(OptionalInt.class, OptionalInt::empty);
+ tmpValuesMap.put(OptionalLong.class, OptionalLong::empty);
+ tmpValuesMap.put(OptionalDouble.class, OptionalDouble::empty);
+
+ DEFAULT_CREATOR_VALUES = Collections.unmodifiableMap(tmpValuesMap);
+ }
+
/**
* Last property model cache to avoid lookup by jsonKey on every access.
*/
@@ -123,13 +152,18 @@
private T createInstance(Class<T> rawType, JsonbCreator creator) {
final T instance;
final List<Object> paramValues = new ArrayList<>();
+ boolean isRecord = ClassMultiReleaseExtension.isRecord(rawType);
for (CreatorModel param : creator.getParams()) {
final ValueWrapper valueWrapper = values.get(param.getName());
//required by spec
- if (valueWrapper == null) {
+ if (!isRecord && valueWrapper == null) {
throw new JsonbException(Messages.getMessage(MessageKeys.JSONB_CREATOR_MISSING_PROPERTY, param.getName()));
+ } else if (valueWrapper == null) {
+ Class<?> rawParamType = ReflectionUtils.getRawType(param.getType());
+ paramValues.add(DEFAULT_CREATOR_VALUES.getOrDefault(rawParamType, NULL_PROVIDER).get());
+ } else {
+ paramValues.add(valueWrapper.getValue());
}
- paramValues.add(valueWrapper.getValue());
}
instance = creator.call(paramValues.toArray(), rawType);
return instance;
diff --git a/src/main/java16/org/eclipse/yasson/internal/ClassMultiReleaseExtension.java b/src/main/java16/org/eclipse/yasson/internal/ClassMultiReleaseExtension.java
index d62e7fb..442ab59 100644
--- a/src/main/java16/org/eclipse/yasson/internal/ClassMultiReleaseExtension.java
+++ b/src/main/java16/org/eclipse/yasson/internal/ClassMultiReleaseExtension.java
@@ -35,7 +35,11 @@
}
static boolean shouldTransformToPropertyName(Method method) {
- return !method.getDeclaringClass().isRecord();
+ return !isRecord(method.getDeclaringClass());
+ }
+
+ public static boolean isRecord(Class<?> clazz) {
+ return clazz.isRecord();
}
static boolean isSpecialAccessorMethod(Method method, Map<String, Property> classProperties) {
diff --git a/src/test/java16/org/eclipse/yasson/records/CarCreatorOptionalTest.java b/src/test/java16/org/eclipse/yasson/records/CarCreatorOptionalTest.java
new file mode 100644
index 0000000..540a777
--- /dev/null
+++ b/src/test/java16/org/eclipse/yasson/records/CarCreatorOptionalTest.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.records;
+
+import java.util.Optional;
+
+import javax.json.bind.annotation.JsonbCreator;
+import javax.json.bind.annotation.JsonbProperty;
+
+public record CarCreatorOptionalTest(String type, int somePrimitive, String color) {
+
+ @JsonbCreator
+ public CarCreatorOptionalTest(@JsonbProperty("type") Optional<String> type,
+ @JsonbProperty("somePrimitive") int somePrimitive,
+ @JsonbProperty("color") String color) {
+ this(type.orElse("typeDefaultValue"), somePrimitive, color);
+ }
+}
diff --git a/src/test/java16/org/eclipse/yasson/records/RecordTest.java b/src/test/java16/org/eclipse/yasson/records/RecordTest.java
index a7c3e97..73d733e 100644
--- a/src/test/java16/org/eclipse/yasson/records/RecordTest.java
+++ b/src/test/java16/org/eclipse/yasson/records/RecordTest.java
@@ -94,4 +94,19 @@
assertEquals(car, deserialized);
}
+ @Test
+ public void testRecordParameterOptionality() {
+ CarCreatorOptionalTest car = new CarCreatorOptionalTest("skoda", 123, "red");
+ String expected = "{\"color\":\"red\",\"somePrimitive\":123,\"type\":\"skoda\"}";
+
+ String json = Jsonbs.defaultJsonb.toJson(car);
+ assertEquals(expected, json);
+
+ String toDeserialize = "{}";
+ String expectedDefaultValues = "{\"color\":null,\"somePrimitive\":0,\"type\":\"typeDefaultValue\"}";
+ CarCreatorOptionalTest deserialized = Jsonbs.defaultJsonb.fromJson(toDeserialize, CarCreatorOptionalTest.class);
+ json = Jsonbs.nullableJsonb.toJson(deserialized);
+ assertEquals(expectedDefaultValues, json);
+ }
+
}