diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 37888591b..564ef3be0 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2021, 2024 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 @@ -24,6 +24,10 @@ jobs: java_version: [ 11, 17 ] steps: + - name: Set up Maven + uses: stCarolas/setup-maven@v4.5 + with: + maven-version: 3.9.6 - name: Checkout for build uses: actions/checkout@v2.3.4 with: diff --git a/.gitignore b/.gitignore index 3160630cb..829533691 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ .idea/ .settings/ /.DS_Store +/.sdkmanrc diff --git a/etc/checkstyle.xml b/etc/checkstyle.xml index 3479136ab..69e6f2fea 100644 --- a/etc/checkstyle.xml +++ b/etc/checkstyle.xml @@ -1,7 +1,7 @@ - + diff --git a/pom.xml b/pom.xml index 0abc66b76..34617a1bd 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ @@ -93,19 +95,19 @@ org.jboss.weld.se weld-se-core - 5.0.0.Final + 5.1.2.Final test org.junit.jupiter junit-jupiter-api - 5.8.2 + ${junit.version} test org.junit.jupiter junit-jupiter-engine - 5.8.2 + ${junit.version} test @@ -118,7 +120,7 @@ Oracle Corporation - http://www.oracle.com/ + https://www.oracle.com/ @@ -139,13 +141,13 @@ Eclipse Public License v. 2.0 - http://www.eclipse.org/legal/epl-v20.html + https://www.eclipse.org/legal/epl-v20.html repo Standard Eclipse License Eclipse Distribution License v. 1.0 - http://www.eclipse.org/org/documents/edl-v10.php + https://www.eclipse.org/org/documents/edl-v10.php repo Standard Eclipse Distribution License @@ -259,19 +261,50 @@ + + + org.codehaus.mojo + build-helper-maven-plugin + 3.5.0 + + + + + add-test-source + generate-test-sources + + add-test-source + + + + ${project.basedir}/src/test/java + ${project.basedir}/src/test/java16 + + + + + + org.apache.maven.plugins maven-compiler-plugin - 3.8.1 + ${maven.compiler.plugin.version} default-testCompile 16 - - ${project.basedir}/src/test/java - ${project.basedir}/src/test/java16 - @@ -345,7 +378,7 @@ org.codehaus.mojo findbugs-maven-plugin - 3.0.4 + 3.0.5 Max Low @@ -364,12 +397,12 @@ org.apache.maven.plugins maven-failsafe-plugin - 3.0.0-M3 + 3.2.5 org.apache.maven.plugins maven-compiler-plugin - 3.8.1 + ${maven.compiler.plugin.version} default-compile @@ -413,7 +446,7 @@ org.apache.maven.plugins maven-jar-plugin - 3.0.2 + 3.3.0 ${project.build.outputDirectory}/META-INF/MANIFEST.MF @@ -427,7 +460,7 @@ org.codehaus.mojo buildnumber-maven-plugin - 1.4 + 3.2.0 {0,date,MM/dd/yyyy hh:mm aa} @@ -446,14 +479,10 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.2.0 + 3.6.3 Yasson ${basedir}/src/main/java/org/eclipse/yasson - - --add-modules - jakarta.json.bind,jakarta.json - 11 @@ -468,7 +497,7 @@ org.apache.maven.plugins maven-source-plugin - 3.0.1 + 3.3.0 attach-sources @@ -481,7 +510,7 @@ org.apache.felix maven-bundle-plugin - 5.1.1 + 5.1.9 osgi-bundle @@ -514,7 +543,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.0.0-M3 + 3.2.5 default-test @@ -549,7 +578,7 @@ - --limit-modules java.base,java.logging,java.sql,jakarta.json.bind,jakarta.json,java.management,jdk.localedata + --limit-modules java.base,java.logging,java.sql,jakarta.json.bind,jakarta.json,java.management,jdk.localedata,org.eclipse.yasson,org.eclipse.parsson **/JavaxNamingExcludedTest.class @@ -562,7 +591,7 @@ org.apache.maven.plugins maven-enforcer-plugin - 3.0.0-M2 + 3.4.1 enforce-versions @@ -577,7 +606,7 @@ [11,) - [3.3.9,) + [3.9.6,) @@ -586,7 +615,7 @@ org.codehaus.mojo build-helper-maven-plugin - 3.0.0 + 3.5.0 add-resource @@ -612,11 +641,10 @@ org.apache.maven.plugins maven-checkstyle-plugin - 3.1.0 + 3.3.1 etc/checkstyle.xml etc/checkstyle-suppressions.xml - UTF-8 true true false @@ -625,7 +653,7 @@ com.puppycrawl.tools checkstyle - 8.29 + 10.13.0 com.sun @@ -638,7 +666,7 @@ org.glassfish.copyright glassfish-copyright-maven-plugin - 2.3 + 2.4 etc/copyright.txt etc/copyright-exclude.txt @@ -697,7 +725,7 @@ org.codehaus.mojo findbugs-maven-plugin - 3.0.4 + 3.0.5 diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index f6c0ace2b..749c14a31 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2024 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 @@ -14,8 +14,8 @@ * Eclipse implementation of the JSONB-API. */ module org.eclipse.yasson { - requires jakarta.json; - requires jakarta.json.bind; + requires transitive jakarta.json; + requires transitive jakarta.json.bind; requires java.logging; requires static java.xml; requires static java.naming; diff --git a/src/main/java/org/eclipse/yasson/FieldAccessStrategy.java b/src/main/java/org/eclipse/yasson/FieldAccessStrategy.java index 1ce711ff1..d624cd13e 100644 --- a/src/main/java/org/eclipse/yasson/FieldAccessStrategy.java +++ b/src/main/java/org/eclipse/yasson/FieldAccessStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2024 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 @@ -28,6 +28,10 @@ * when SecurityManager is turned on.

*/ public class FieldAccessStrategy implements PropertyVisibilityStrategy { + + FieldAccessStrategy() { + } + @Override public boolean isVisible(Field field) { return true; diff --git a/src/main/java/org/eclipse/yasson/JsonBindingProvider.java b/src/main/java/org/eclipse/yasson/JsonBindingProvider.java index 3e4f4d406..47add9b44 100644 --- a/src/main/java/org/eclipse/yasson/JsonBindingProvider.java +++ b/src/main/java/org/eclipse/yasson/JsonBindingProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024 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 @@ -22,6 +22,9 @@ */ public class JsonBindingProvider extends JsonbProvider { + public JsonBindingProvider() { + } + @Override public JsonbBuilder create() { return new JsonBindingBuilder(); diff --git a/src/main/java/org/eclipse/yasson/YassonConfig.java b/src/main/java/org/eclipse/yasson/YassonConfig.java index 9387eeab8..eea7f1f0d 100644 --- a/src/main/java/org/eclipse/yasson/YassonConfig.java +++ b/src/main/java/org/eclipse/yasson/YassonConfig.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022 IBM and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024 IBM 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 @@ -21,6 +21,9 @@ * Custom properties for configuring Yasson outside of the specification {@link jakarta.json.bind.JsonbConfig} scope. */ public class YassonConfig extends JsonbConfig { + + public YassonConfig() { + } /** * @see #withFailOnUnknownProperties(boolean) diff --git a/src/main/java/org/eclipse/yasson/YassonJsonb.java b/src/main/java/org/eclipse/yasson/YassonJsonb.java index f484300fa..cdd062ed0 100644 --- a/src/main/java/org/eclipse/yasson/YassonJsonb.java +++ b/src/main/java/org/eclipse/yasson/YassonJsonb.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024 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 @@ -12,6 +12,7 @@ package org.eclipse.yasson; +import java.io.IOException; import java.lang.reflect.Type; import jakarta.json.JsonStructure; @@ -131,4 +132,13 @@ public interface YassonJsonb extends jakarta.json.bind.Jsonb { * @since JSON Binding 1.0 */ JsonStructure toJsonStructure(Object object, Type runtimeType) throws JsonbException; + + /** + * Closes this Jsonb instance and releases any system resources associated. + * See @{@link jakarta.json.bind.Jsonb} and @{@link jakarta.json.bind.Jsonb#close()} for more details on closing. + * + * @throws IOException if an I/O error occurs; reducing from @{@link Exception} in @{@link AutoCloseable}. + */ + @Override + void close() throws IOException; } diff --git a/src/main/java/org/eclipse/yasson/YassonProperties.java b/src/main/java/org/eclipse/yasson/YassonProperties.java index d1a98792d..50a066578 100644 --- a/src/main/java/org/eclipse/yasson/YassonProperties.java +++ b/src/main/java/org/eclipse/yasson/YassonProperties.java @@ -1,6 +1,6 @@ /* - * Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2019, 2020 Payara Foundation and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024 Payara Foundation 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 @@ -24,27 +24,31 @@ private YassonProperties() { } /** - * @deprecated + * @deprecated Use {@link YassonConfig#FAIL_ON_UNKNOWN_PROPERTIES} instead * @see YassonConfig#withFailOnUnknownProperties(boolean) */ + @Deprecated public static final String FAIL_ON_UNKNOWN_PROPERTIES = YassonConfig.FAIL_ON_UNKNOWN_PROPERTIES; /** - * @deprecated + * @deprecated Use {@link YassonConfig#USER_TYPE_MAPPING} instead * @see YassonConfig#withUserTypeMapping(java.util.Map) */ + @Deprecated public static final String USER_TYPE_MAPPING = YassonConfig.USER_TYPE_MAPPING; /** - * @deprecated + * @deprecated Use {@link YassonConfig#ZERO_TIME_PARSE_DEFAULTING} instead * @see YassonConfig#withZeroTimeParseDefaulting(boolean) */ + @Deprecated public static final String ZERO_TIME_PARSE_DEFAULTING = YassonConfig.ZERO_TIME_PARSE_DEFAULTING; /** - * @deprecated + * @deprecated Use {@link YassonConfig#NULL_ROOT_SERIALIZER} instead * @see YassonConfig#withNullRootSerializer(jakarta.json.bind.serializer.JsonbSerializer) */ + @Deprecated public static final String NULL_ROOT_SERIALIZER = YassonConfig.NULL_ROOT_SERIALIZER; } diff --git a/src/main/java/org/eclipse/yasson/internal/AnnotationIntrospector.java b/src/main/java/org/eclipse/yasson/internal/AnnotationIntrospector.java index 36c29431e..2d61af4f6 100644 --- a/src/main/java/org/eclipse/yasson/internal/AnnotationIntrospector.java +++ b/src/main/java/org/eclipse/yasson/internal/AnnotationIntrospector.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024 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 @@ -84,11 +84,10 @@ */ public class AnnotationIntrospector { - // private static final Set> OPTIONALS = Set.of(Optional.class, - // OptionalInt.class, - // OptionalLong.class, - // OptionalDouble.class); - + /*private static final Set> OPTIONALS = Set.of(Optional.class, + OptionalInt.class, + OptionalLong.class, + OptionalDouble.class);*/ private final JsonbContext jsonbContext; private final ConstructorPropertiesAnnotationIntrospector constructorPropertiesIntrospector; @@ -206,10 +205,10 @@ JsonbCreator createJsonbCreator(Executable executable, JsonbCreator existing, Cl final Parameter parameter = parameters[i]; final JsonbProperty jsonbPropertyAnnotation = parameter.getAnnotation(JsonbProperty.class); if (jsonbPropertyAnnotation != null && !jsonbPropertyAnnotation.value().isEmpty()) { - creatorModels[i] = new CreatorModel(jsonbPropertyAnnotation.value(), parameter, executable, jsonbContext); + creatorModels[i] = new CreatorModel(jsonbPropertyAnnotation.value(), parameter, /*executable,*/ jsonbContext); } else { final String translatedParameterName = propertyNamingStrategy.translateName(parameter.getName()); - creatorModels[i] = new CreatorModel(translatedParameterName, parameter, executable, jsonbContext); + creatorModels[i] = new CreatorModel(translatedParameterName, parameter, /*executable,*/ jsonbContext); } } @@ -222,15 +221,30 @@ JsonbCreator createJsonbCreator(Executable executable, JsonbCreator existing, Cl * @param property property not null * @return components info */ - public AdapterBinding getAdapterBinding(Property property) { + public AdapterBinding getAdapterBinding(Property property) { Objects.requireNonNull(property); - JsonbTypeAdapter adapterAnnotation = getAnnotationFromProperty(JsonbTypeAdapter.class, property) - .orElseGet(() -> getAnnotationFromPropertyType(property, JsonbTypeAdapter.class)); + return getAdapterBindingFromAnnotation(getAnnotationFromProperty(JsonbTypeAdapter.class, property) + .orElseGet(() -> getAnnotationFromPropertyType(property, JsonbTypeAdapter.class)), + ReflectionUtils.getOptionalRawType(property.getPropertyType())); + } + + private AdapterBinding getAdapterBindingFromAnnotation(JsonbTypeAdapter adapterAnnotation, Optional> expectedClass) { if (adapterAnnotation == null) { return null; } - return getAdapterBindingFromAnnotation(adapterAnnotation, ReflectionUtils.getOptionalRawType(property.getPropertyType())); + @SuppressWarnings("rawtypes") + final Class adapterClass = adapterAnnotation.value(); + @SuppressWarnings("unchecked") + final AdapterBinding adapterBinding = jsonbContext.getComponentMatcher().introspectAdapterBinding(adapterClass, null); + + expectedClass.ifPresent(clazz -> { + if (!ReflectionUtils.getRawType(adapterBinding.getBindingType()).isAssignableFrom(clazz)) { + throw new JsonbException(Messages.getMessage(MessageKeys.ADAPTER_INCOMPATIBLE, + adapterBinding.getBindingType(), clazz)); + } + }); + return adapterBinding; } /** @@ -239,28 +253,10 @@ public AdapterBinding getAdapterBinding(Property property) { * @param clsElement type not null * @return components info */ - public AdapterBinding getAdapterBinding(JsonbAnnotatedElement> clsElement) { + public AdapterBinding getAdapterBinding(JsonbAnnotatedElement> clsElement) { Objects.requireNonNull(clsElement); - - JsonbTypeAdapter adapterAnnotation = clsElement.getElement().getAnnotation(JsonbTypeAdapter.class); - if (adapterAnnotation == null) { - return null; - } - - return getAdapterBindingFromAnnotation(adapterAnnotation, Optional.ofNullable(clsElement.getElement())); - } - - private AdapterBinding getAdapterBindingFromAnnotation(JsonbTypeAdapter adapterAnnotation, Optional> expectedClass) { - final Class adapterClass = adapterAnnotation.value(); - final AdapterBinding adapterBinding = jsonbContext.getComponentMatcher().introspectAdapterBinding(adapterClass, null); - - if (expectedClass.isPresent() && !( - ReflectionUtils.getRawType(adapterBinding.getBindingType()).isAssignableFrom(expectedClass.get()))) { - throw new JsonbException(Messages.getMessage(MessageKeys.ADAPTER_INCOMPATIBLE, - adapterBinding.getBindingType(), - expectedClass.get())); - } - return adapterBinding; + return getAdapterBindingFromAnnotation(clsElement.getElement().getAnnotation(JsonbTypeAdapter.class), + Optional.ofNullable(clsElement.getElement())); } /** @@ -269,16 +265,22 @@ private AdapterBinding getAdapterBindingFromAnnotation(JsonbTypeAdapter adapterA * @param property property not null * @return components info */ - public DeserializerBinding getDeserializerBinding(Property property) { + public DeserializerBinding getDeserializerBinding(Property property) { Objects.requireNonNull(property); - JsonbTypeDeserializer deserializerAnnotation = getAnnotationFromProperty(JsonbTypeDeserializer.class, property) - .orElseGet(() -> getAnnotationFromPropertyType(property, JsonbTypeDeserializer.class)); + return getDeserializerBindingFromAnnotation(getAnnotationFromProperty(JsonbTypeDeserializer.class, property) + .orElseGet(() -> getAnnotationFromPropertyType(property, JsonbTypeDeserializer.class))); + } + + private DeserializerBinding getDeserializerBindingFromAnnotation(JsonbTypeDeserializer deserializerAnnotation) { if (deserializerAnnotation == null) { return null; } + @SuppressWarnings("rawtypes") final Class deserializerClass = deserializerAnnotation.value(); - return jsonbContext.getComponentMatcher().introspectDeserializerBinding(deserializerClass, null); + @SuppressWarnings("unchecked") + DeserializerBinding deserializerBinding = jsonbContext.getComponentMatcher().introspectDeserializerBinding(deserializerClass, null); + return deserializerBinding; } /** @@ -289,15 +291,8 @@ public DeserializerBinding getDeserializerBinding(Property property) { */ public DeserializerBinding getDeserializerBinding(Parameter parameter) { Objects.requireNonNull(parameter); - JsonbTypeDeserializer deserializerAnnotation = - Optional.ofNullable(parameter.getDeclaredAnnotation(JsonbTypeDeserializer.class)) - .orElseGet(() -> getAnnotationFromParameterType(parameter, JsonbTypeDeserializer.class)); - if (deserializerAnnotation == null) { - return null; - } - - final Class deserializerClass = deserializerAnnotation.value(); - return jsonbContext.getComponentMatcher().introspectDeserializerBinding(deserializerClass, null); + return getDeserializerBindingFromAnnotation(Optional.ofNullable(parameter.getDeclaredAnnotation(JsonbTypeDeserializer.class)) + .orElseGet(() -> getAnnotationFromParameterType(parameter, JsonbTypeDeserializer.class))); } /** @@ -306,16 +301,11 @@ public DeserializerBinding getDeserializerBinding(Parameter parameter) { * @param parameter parameter not null * @return components info */ - public AdapterBinding getAdapterBinding(Parameter parameter) { + public AdapterBinding getAdapterBinding(Parameter parameter) { Objects.requireNonNull(parameter); - JsonbTypeAdapter adapter = - Optional.ofNullable(parameter.getDeclaredAnnotation(JsonbTypeAdapter.class)) - .orElseGet(() -> getAnnotationFromParameterType(parameter, JsonbTypeAdapter.class)); - if (adapter == null) { - return null; - } - - return getAdapterBindingFromAnnotation(adapter, ReflectionUtils.getOptionalRawType(parameter.getParameterizedType())); + return getAdapterBindingFromAnnotation(Optional.ofNullable(parameter.getDeclaredAnnotation(JsonbTypeAdapter.class)) + .orElseGet(() -> getAnnotationFromParameterType(parameter, JsonbTypeAdapter.class)), + ReflectionUtils.getOptionalRawType(parameter.getParameterizedType())); } private T getAnnotationFromParameterType(Parameter parameter, Class annotationClass) { @@ -331,15 +321,9 @@ private T getAnnotationFromParameterType(Parameter parame * @param clsElement type not null * @return components info */ - public DeserializerBinding getDeserializerBinding(JsonbAnnotatedElement> clsElement) { + public DeserializerBinding getDeserializerBinding(JsonbAnnotatedElement> clsElement) { Objects.requireNonNull(clsElement); - JsonbTypeDeserializer deserializerAnnotation = clsElement.getElement().getAnnotation(JsonbTypeDeserializer.class); - if (deserializerAnnotation == null) { - return null; - } - - final Class deserializerClass = deserializerAnnotation.value(); - return jsonbContext.getComponentMatcher().introspectDeserializerBinding(deserializerClass, null); + return getDeserializerBindingFromAnnotation(clsElement.getElement().getAnnotation(JsonbTypeDeserializer.class)); } /** @@ -348,17 +332,22 @@ public DeserializerBinding getDeserializerBinding(JsonbAnnotatedElement * @param property property not null * @return components info */ - public SerializerBinding getSerializerBinding(Property property) { + public SerializerBinding getSerializerBinding(Property property) { Objects.requireNonNull(property); - JsonbTypeSerializer serializerAnnotation = getAnnotationFromProperty(JsonbTypeSerializer.class, property) - .orElseGet(() -> getAnnotationFromPropertyType(property, JsonbTypeSerializer.class)); + return getSerializerBindingFromAnnotation(getAnnotationFromProperty(JsonbTypeSerializer.class, property) + .orElseGet(() -> getAnnotationFromPropertyType(property, JsonbTypeSerializer.class))); + } + + private SerializerBinding getSerializerBindingFromAnnotation(JsonbTypeSerializer serializerAnnotation) { if (serializerAnnotation == null) { return null; } + @SuppressWarnings("rawtypes") final Class serializerClass = serializerAnnotation.value(); - return jsonbContext.getComponentMatcher().introspectSerializerBinding(serializerClass, null); - + @SuppressWarnings("unchecked") + SerializerBinding serializerBinding = jsonbContext.getComponentMatcher().introspectSerializerBinding(serializerClass, null); + return serializerBinding; } /** @@ -367,24 +356,15 @@ public SerializerBinding getSerializerBinding(Property property) { * @param clsElement type not null * @return components info */ - public SerializerBinding getSerializerBinding(JsonbAnnotatedElement> clsElement) { + public SerializerBinding getSerializerBinding(JsonbAnnotatedElement> clsElement) { Objects.requireNonNull(clsElement); - JsonbTypeSerializer serializerAnnotation = clsElement.getElement().getAnnotation(JsonbTypeSerializer.class); - if (serializerAnnotation == null) { - return null; - } - - final Class serializerClass = serializerAnnotation.value(); - return jsonbContext.getComponentMatcher().introspectSerializerBinding(serializerClass, null); + return getSerializerBindingFromAnnotation(clsElement.getElement().getAnnotation(JsonbTypeSerializer.class)); } private T getAnnotationFromPropertyType(Property property, Class annotationClass) { final Optional> optionalRawType = ReflectionUtils.getOptionalRawType(property.getPropertyType()); - if (!optionalRawType.isPresent()) { - //will not work for type variable properties, which are bound to class that is annotated. - return null; - } - return findAnnotation(collectAnnotations(optionalRawType.get()).getAnnotations(), annotationClass); + //will not work for type variable properties, which are bound to class that is annotated. + return optionalRawType.map(aClass -> findAnnotation(collectAnnotations(aClass).getAnnotations(), annotationClass)).orElse(null); } /** @@ -403,8 +383,9 @@ public Optional isPropertyNillable(Property property) { return nillable.map(JsonbNillable::value); } final Optional jsonbProperty = getAnnotationFromProperty(JsonbProperty.class, property); - return jsonbProperty.map(JsonbProperty::nillable); - + @SuppressWarnings("deprecation") + Optional optionalBoolean = jsonbProperty.map(JsonbProperty::nillable); + return optionalBoolean; } /** @@ -453,7 +434,7 @@ public EnumSet getJsonbTransientCategorized(Property property) Map annotationFromPropertyCategorized = getAnnotationFromPropertyCategorized( JsonbTransient.class, property); - if (annotationFromPropertyCategorized.size() > 0) { + if (!annotationFromPropertyCategorized.isEmpty()) { transientTarget.addAll(annotationFromPropertyCategorized.keySet()); return transientTarget; } @@ -476,21 +457,19 @@ public Map getJsonbDateFormatCategorized(P Map annotationFromPropertyCategorized = getAnnotationFromPropertyCategorized( JsonbDateFormat.class, property); - if (annotationFromPropertyCategorized.size() != 0) { + if (!annotationFromPropertyCategorized.isEmpty()) { annotationFromPropertyCategorized.forEach((key, annotation) -> result .put(key, createJsonbDateFormatter(annotation.value(), annotation.locale(), property))); } // No date format on property, try class level // if property is not TypeVariable and its class is not date skip it - final Optional> propertyRawTypeOptional = ReflectionUtils.getOptionalRawType(property.getPropertyType()); - if (propertyRawTypeOptional.isPresent()) { - Class rawType = propertyRawTypeOptional.get(); - if (!( - Date.class.isAssignableFrom(rawType) || Calendar.class.isAssignableFrom(rawType) - || TemporalAccessor.class.isAssignableFrom(rawType))) { - return new HashMap<>(); - } + Map map = ReflectionUtils.getOptionalRawType(property.getPropertyType()).map(rawType -> + (Date.class.isAssignableFrom(rawType) || Calendar.class.isAssignableFrom(rawType) + || TemporalAccessor.class.isAssignableFrom(rawType)) ? null : new HashMap()) + .orElse(null); + if (map != null) { + return map; } JsonbDateFormat classLevelDateFormatter = findAnnotation(property.getDeclaringClassElement().getAnnotations(), @@ -546,19 +525,19 @@ public Map getJsonNumberFormatter(Proper Map annotationFromPropertyCategorized = getAnnotationFromPropertyCategorized( JsonbNumberFormat.class, property); - // if (annotationFromPropertyCategorized.size() == 0) { - // final Optional> propertyRawTypeOptional = ReflectionUtils.getOptionalRawType(property - // .getPropertyType()); - // if (propertyRawTypeOptional.isPresent()) { - // Class rawType = propertyRawTypeOptional.get(); - // if (!Number.class.isAssignableFrom(rawType)) { - // return new HashMap<>(); - // } - // } - // } else { - // annotationFromPropertyCategorized.forEach((key, annotation) -> result - // .put(key, new JsonbNumberFormatter(annotation.value(), annotation.locale()))); - // } + /*if (annotationFromPropertyCategorized.isEmpty()) { + final Optional> propertyRawTypeOptional = ReflectionUtils.getOptionalRawType(property + .getPropertyType()); + if (propertyRawTypeOptional.isPresent()) { + Class rawType = propertyRawTypeOptional.get(); + if (!Number.class.isAssignableFrom(rawType)) { + return new HashMap<>(); + } + } + } else { + annotationFromPropertyCategorized.forEach((key, annotation) -> result + .put(key, new JsonbNumberFormatter(annotation.value(), annotation.locale()))); + }*/ annotationFromPropertyCategorized.forEach((key, annotation) -> result .put(key, new JsonbNumberFormatter(annotation.value(), annotation.locale()))); @@ -750,7 +729,7 @@ private T getMethodAnnotation(Class annotationClass, J return findAnnotation(methodElement.getAnnotations(), annotationClass); } - private void collectFromInterfaces(Class annotationClass, + /*private void collectFromInterfaces(Class annotationClass, Class clazz, Map, T> collectedAnnotations) { @@ -761,7 +740,7 @@ private void collectFromInterfaces(Class annotationCla } collectFromInterfaces(annotationClass, interfaceClass, collectedAnnotations); } - } + }*/ /** * Get class interfaces recursively. @@ -906,7 +885,7 @@ public JsonbAnnotatedElement> collectAnnotations(Class clazz) { } if (!clazz.isPrimitive() && !clazz.isArray() && (clazz.getPackage() != null)) { - addIfNotPresent(classElement, null, clazz.getPackage().getAnnotations()); + addIfNotPresent(classElement, /*null,*/ clazz.getPackage().getAnnotations()); } return classElement; } @@ -941,48 +920,46 @@ private Map, LinkedList>> colle return map; } - // private void collectParentInterfaceAnnotations(Class currentInterf, - // Map, LinkedList> overall) { - // Map, LinkedList> parents = new HashMap<>(); - // for (Class parentInterf : currentInterf.getInterfaces()) { - // collectParentInterfaceAnnotations(parentInterf, ); - // current.entrySet().stream() - // .filter(entry -> parents.containsKey(entry.getKey()) || REPEATABLE.contains(entry.getKey())) - // .peek(entry -> { - // if (parents.containsKey(entry.getKey())) { - // throw new JsonbException("CHANGE THIS EXCEPTION"); - // } - // }) - // .forEach(entry -> { - // parents.computeIfAbsent(entry.getKey(), aClass -> new LinkedList<>()).addAll(entry.getValue()); - // map.computeIfAbsent(entry.getKey(), aClass -> new LinkedList<>()).addAll(entry.getValue()); - // }); - // } - // if (currentInterf.isInterface()) { - // for (Annotation annotation : currentInterf.getDeclaredAnnotations()) { - // map.computeIfAbsent(annotation.annotationType(), aClass -> new LinkedList<>()).add(annotation); - // } - // } - // return map; - // } - - private void addIfNotPresent(JsonbAnnotatedElement element, Class definedType, Annotation... annotations) { - for (Annotation annotation : annotations) { - if (element.getAnnotation(annotation.annotationType()).isEmpty() - || REPEATABLE.contains(annotation.annotationType())) { - element.putAnnotation(annotation, true, definedType); + /*private void collectParentInterfaceAnnotations(Class currentInterf, + Map, LinkedList> overall) { + Map, LinkedList> parents = new HashMap<>(); + for (Class parentInterf : currentInterf.getInterfaces()) { + collectParentInterfaceAnnotations(parentInterf, ); + current.entrySet().stream() + .filter(entry -> parents.containsKey(entry.getKey()) || REPEATABLE.contains(entry.getKey())) + .peek(entry -> { + if (parents.containsKey(entry.getKey())) { + throw new JsonbException("CHANGE THIS EXCEPTION"); + } + }) + .forEach(entry -> { + parents.computeIfAbsent(entry.getKey(), aClass -> new LinkedList<>()).addAll(entry.getValue()); + map.computeIfAbsent(entry.getKey(), aClass -> new LinkedList<>()).addAll(entry.getValue()); + }); } - } + if (currentInterf.isInterface()) { + for (Annotation annotation : currentInterf.getDeclaredAnnotations()) { + map.computeIfAbsent(annotation.annotationType(), aClass -> new LinkedList<>()).add(annotation); + } + } + return map; + }*/ + + private void addIfNotPresent(JsonbAnnotatedElement element, /*Class definedType,*/ Annotation... annotations) { + Arrays.stream(annotations) + .filter(annotation -> element.getAnnotation(annotation.annotationType()).isEmpty() + || REPEATABLE.contains(annotation.annotationType())) + .forEach(annotation -> element.putAnnotation(annotation, true, null/*definedType*/)); } - public boolean requiredParameters(Executable executable, JsonbAnnotatedElement annotated) { + public boolean requiredParameters(/*Executable executable, JsonbAnnotatedElement annotated*/) { return jsonbContext.getConfigProperties().hasRequiredCreatorParameters(); - // if (OPTIONALS.contains(annotated.getElement().getType())) { - // return false; - // } - // return annotated.getAnnotation(JsonbRequired.class) - // .or(() -> Optional.ofNullable(executable.getAnnotation(JsonbRequired.class))) - // .map(JsonbRequired::value) - // .orElseGet(() -> jsonbContext.getConfigProperties().hasRequiredCreatorParameters()); + /* if (OPTIONALS.contains(annotated.getElement().getType())) { + return false; + } + return annotated.getAnnotation(JsonbRequired.class) + .or(() -> Optional.ofNullable(executable.getAnnotation(JsonbRequired.class))) + .map(JsonbRequired::value) + .orElseGet(() -> jsonbContext.getConfigProperties().hasRequiredCreatorParameters());*/ } } diff --git a/src/main/java/org/eclipse/yasson/internal/ComponentMatcher.java b/src/main/java/org/eclipse/yasson/internal/ComponentMatcher.java index b4a2cf831..b4db8a7bf 100644 --- a/src/main/java/org/eclipse/yasson/internal/ComponentMatcher.java +++ b/src/main/java/org/eclipse/yasson/internal/ComponentMatcher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024 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 @@ -15,12 +15,15 @@ import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; +import java.util.Arrays; import java.util.LinkedList; import java.util.Objects; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.function.BiFunction; import java.util.function.Function; +import java.util.stream.Stream; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.adapter.JsonbAdapter; @@ -46,7 +49,7 @@ public class ComponentMatcher { */ private volatile boolean genericComponents; - private final ConcurrentMap userComponents; + private final ConcurrentMap> userComponents; /** * Create component matcher. @@ -60,64 +63,72 @@ public class ComponentMatcher { init(); } + private interface ComponentBindingsFunction> + extends BiFunction, B, ComponentBindings> {} + /** * Called during context creation, introspecting user components provided with JsonbConfig. */ void init() { + //Process serializers final JsonbSerializer[] serializers = (JsonbSerializer[]) jsonbContext.getConfig() .getProperty(JsonbConfig.SERIALIZERS).orElseGet(() -> new JsonbSerializer[] {}); - for (JsonbSerializer serializer : serializers) { - SerializerBinding serializerBinding = introspectSerializerBinding(serializer.getClass(), serializer); - addSerializer(serializerBinding.getBindingType(), serializerBinding); - } + @SuppressWarnings("unchecked") + Function, SerializerBinding> introspectSerializerFunction = (serializer) -> + introspectSerializerBinding(serializer.getClass(), serializer); + @SuppressWarnings({"unchecked", "rawtypes"}) + ComponentBindingsFunction, SerializerBinding> createSerializerBindingFunction = (bindings, newBinding) -> + new ComponentBindings(bindings, newBinding); + addToComponentBindings(serializers, introspectSerializerFunction, ComponentBindings::getSerializerBinding, createSerializerBindingFunction); + + //Process deserializers final JsonbDeserializer[] deserializers = (JsonbDeserializer[]) jsonbContext.getConfig() .getProperty(JsonbConfig.DESERIALIZERS).orElseGet(() -> new JsonbDeserializer[] {}); - for (JsonbDeserializer deserializer : deserializers) { - DeserializerBinding deserializerBinding = introspectDeserializerBinding(deserializer.getClass(), deserializer); - addDeserializer(deserializerBinding.getBindingType(), deserializerBinding); - } - + @SuppressWarnings("unchecked") + Function, DeserializerBinding> introspectDeserializerFunction = (deserializer) -> + introspectDeserializerBinding(deserializer.getClass(), deserializer); + @SuppressWarnings({"unchecked", "rawtypes"}) + ComponentBindingsFunction, DeserializerBinding> createDeserializerBindingFunction = (bindings, newBinding) -> + new ComponentBindings(bindings, newBinding); + addToComponentBindings(deserializers, introspectDeserializerFunction, ComponentBindings::getDeserializerBinding, createDeserializerBindingFunction); + + //Process adapters final JsonbAdapter[] adapters = (JsonbAdapter[]) jsonbContext.getConfig().getProperty(JsonbConfig.ADAPTERS) .orElseGet(() -> new JsonbAdapter[] {}); - for (JsonbAdapter adapter : adapters) { - AdapterBinding adapterBinding = introspectAdapterBinding(adapter.getClass(), adapter); - addAdapter(adapterBinding.getBindingType(), adapterBinding); - } + @SuppressWarnings("unchecked") + Function, AdapterBinding> introspectAdapterFunction = (adapter) -> introspectAdapterBinding(adapter.getClass(), adapter); + @SuppressWarnings({"unchecked", "rawtypes"}) + ComponentBindingsFunction, AdapterBinding> createAdapterBindingFunction = (bindings, newBinding) -> + new ComponentBindings(bindings, newBinding); + addToComponentBindings(adapters, introspectAdapterFunction, ComponentBindings::getAdapterBinding, createAdapterBindingFunction); } - private ComponentBindings getBindingInfo(Type type) { + private ComponentBindings getBindingInfo(Type type) { return userComponents - .compute(type, (type1, bindingInfo) -> bindingInfo != null ? bindingInfo : new ComponentBindings(type1)); + .compute(type, (type1, bindingInfo) -> bindingInfo != null ? bindingInfo : new ComponentBindings<>(type1)); } - private void addSerializer(Type bindingType, SerializerBinding serializer) { - userComponents.computeIfPresent(bindingType, (type, bindings) -> { - if (bindings.getSerializer() != null) { - return bindings; - } - registerGeneric(bindingType); - return new ComponentBindings(bindingType, serializer, bindings.getDeserializer(), bindings.getAdapterInfo()); - }); - } + private > void addToComponentBindings(C[] customisations, Function introspectFunction, + Function, B> getExistingBinding, + ComponentBindingsFunction createNewComponentBindings) { - private void addDeserializer(Type bindingType, DeserializerBinding deserializer) { - userComponents.computeIfPresent(bindingType, (type, bindings) -> { - if (bindings.getDeserializer() != null) { - return bindings; - } - registerGeneric(bindingType); - return new ComponentBindings(bindingType, bindings.getSerializer(), deserializer, bindings.getAdapterInfo()); - }); - } + Objects.requireNonNull(customisations); + Objects.requireNonNull(introspectFunction); + Objects.requireNonNull(getExistingBinding); + Objects.requireNonNull(createNewComponentBindings); - private void addAdapter(Type bindingType, AdapterBinding adapter) { - userComponents.computeIfPresent(bindingType, (type, bindings) -> { - if (bindings.getAdapterInfo() != null) { - return bindings; - } - registerGeneric(bindingType); - return new ComponentBindings(bindingType, bindings.getSerializer(), bindings.getDeserializer(), adapter); - }); + for (C customisation : customisations) { + B componentBinding = introspectFunction.apply(customisation); + Type bindingType = componentBinding.getBindingType(); + + userComponents.computeIfPresent(bindingType, (type, bindings) -> { + if (getExistingBinding.apply(bindings) != null) { + return bindings; + } + registerGeneric(bindingType); + return createNewComponentBindings.apply(bindings, componentBinding); + }); + } } /** @@ -142,7 +153,7 @@ public Optional> getSerializerBinding(Type propertyRuntimeT ComponentBoundCustomization customization) { if (customization == null || customization.getSerializerBinding() == null) { - return searchComponentBinding(propertyRuntimeType, ComponentBindings::getSerializer); + return searchComponentBinding(propertyRuntimeType, ComponentBindings::getSerializerBinding); } return Optional.of(customization.getSerializerBinding()); } @@ -157,7 +168,7 @@ public Optional> getSerializerBinding(Type propertyRuntimeT public Optional> getDeserializerBinding(Type propertyRuntimeType, ComponentBoundCustomization customization) { if (customization == null || customization.getDeserializerBinding() == null) { - return searchComponentBinding(propertyRuntimeType, ComponentBindings::getDeserializer); + return searchComponentBinding(propertyRuntimeType, ComponentBindings::getDeserializerBinding); } return Optional.of(customization.getDeserializerBinding()); } @@ -170,10 +181,10 @@ public Optional> getDeserializerBinding(Type propertyRunt * @param customization customization with component info * @return components info if present */ - public Optional getSerializeAdapterBinding(Type propertyRuntimeType, + public Optional> getSerializeAdapterBinding(Type propertyRuntimeType, ComponentBoundCustomization customization) { if (customization == null || customization.getSerializeAdapterBinding() == null) { - return searchComponentBinding(propertyRuntimeType, ComponentBindings::getAdapterInfo); + return searchComponentBinding(propertyRuntimeType, ComponentBindings::getAdapterBinding); } return Optional.of(customization.getSerializeAdapterBinding()); } @@ -186,54 +197,55 @@ public Optional getSerializeAdapterBinding(Type propertyRuntimeT * @param customization customization with component info * @return components info if present */ - public Optional getDeserializeAdapterBinding(Type propertyRuntimeType, + public Optional> getDeserializeAdapterBinding(Type propertyRuntimeType, ComponentBoundCustomization customization) { if (customization == null || customization.getDeserializeAdapterBinding() == null) { - return searchComponentBinding(propertyRuntimeType, ComponentBindings::getAdapterInfo); + return searchComponentBinding(propertyRuntimeType, ComponentBindings::getAdapterBinding); } return Optional.of(customization.getDeserializeAdapterBinding()); } - private Optional searchComponentBinding(Type runtimeType, Function supplier) { + private > Optional searchComponentBinding(Type runtimeType, + Function, T> bindingGetter) { // First check if there is an exact match - ComponentBindings binding = userComponents.get(runtimeType); - if (binding != null) { - Optional match = getMatchingBinding(runtimeType, binding, supplier); - if (match.isPresent()) { - return match; - } + Optional match = getMatchingBinding(runtimeType, bindingGetter); + if (match.isPresent()) { + return match; } - + Optional> runtimeClass = ReflectionUtils.getOptionalRawType(runtimeType); - if (runtimeClass.isPresent()) { + return runtimeClass.map(clazz -> { // Check if any interfaces have a match - for (Class ifc : runtimeClass.get().getInterfaces()) { - ComponentBindings ifcBinding = userComponents.get(ifc); - if (ifcBinding != null) { - Optional match = getMatchingBinding(ifc, ifcBinding, supplier); - if (match.isPresent()) { - return match; - } - } + Optional interfaceMatch = findBindingInClasses(Arrays.stream(clazz.getInterfaces()), ifc -> getMatchingBinding(ifc, bindingGetter)); + if (interfaceMatch.isPresent()) { + return interfaceMatch; } - + // check if the superclass has a match - Class superClass = runtimeClass.get().getSuperclass(); + Class superClass = clazz.getSuperclass(); if (superClass != null && superClass != Object.class) { - Optional superBinding = searchComponentBinding(superClass, supplier); + Optional superBinding = + findBindingInClasses(Stream.of(superClass), superClazz -> searchComponentBinding(superClazz, bindingGetter)); if (superBinding.isPresent()) { return superBinding; } } - } - - return Optional.empty(); + return Optional.empty(); + }).orElse(Optional.empty()); + } + + private Optional findBindingInClasses(Stream> stream, Function, Optional> mapper) { + return stream + .map(mapper) + .filter(Optional::isPresent) + .findFirst().orElse(Optional.empty()); } - - private Optional getMatchingBinding(Type runtimeType, ComponentBindings binding, Function supplier) { - final T component = supplier.apply(binding); - if (component != null && matches(runtimeType, binding.getBindingType())) { - return Optional.of(component); + + private > Optional getMatchingBinding(Type runtimeType, + Function, T> bindingGetter) { + ComponentBindings binding = userComponents.get(runtimeType); + if (binding != null) { + return (matches(runtimeType, binding.getBindingType())) ? Optional.ofNullable(bindingGetter.apply(binding)) : Optional.empty(); } return Optional.empty(); } @@ -275,6 +287,27 @@ private boolean matchTypeArguments(ParameterizedType requiredType, Parameterized return true; } + private > B introspectBinding(Class customisationClass, T customisationInstance, + Class customisationClassToFind, Function, B> getExistingBinding, + BiFunction createNewBinding) { + + Objects.requireNonNull(customisationClass); + Objects.requireNonNull(customisationClassToFind); + Objects.requireNonNull(getExistingBinding); + Objects.requireNonNull(createNewBinding); + final ParameterizedType customisationRuntimeType = ReflectionUtils.findParameterizedType(customisationClass, customisationClassToFind); + Type customisationBindingType = resolveTypeArg(customisationRuntimeType.getActualTypeArguments()[0], customisationClass); + final ComponentBindings componentBindings = getBindingInfo(customisationBindingType); + B binding = getExistingBinding.apply(componentBindings); + if (binding != null && customisationClass.equals(binding.getComponentClass())) { + return binding; + } else { + T customisation = customisationInstance != null ? customisationInstance : jsonbContext.getComponentInstanceCreator() + .getOrCreateComponent(customisationClass); + return createNewBinding.apply(customisationRuntimeType.getActualTypeArguments(), customisation); + } + } + /** * Introspect components generic information and put resolved types into metadata wrapper. * @@ -282,20 +315,9 @@ private boolean matchTypeArguments(ParameterizedType requiredType, Parameterized * @param instance components instance * @return introspected info with resolved typevar types. */ - AdapterBinding introspectAdapterBinding(Class adapterClass, JsonbAdapter instance) { - final ParameterizedType adapterRuntimeType = ReflectionUtils.findParameterizedType(adapterClass, JsonbAdapter.class); - final Type[] adapterTypeArguments = adapterRuntimeType.getActualTypeArguments(); - Type adaptFromType = resolveTypeArg(adapterTypeArguments[0], adapterClass); - Type adaptToType = resolveTypeArg(adapterTypeArguments[1], adapterClass); - final ComponentBindings componentBindings = getBindingInfo(adaptFromType); - if (componentBindings.getAdapterInfo() != null && componentBindings.getAdapterInfo().getAdapter().getClass() - .equals(adapterClass)) { - return componentBindings.getAdapterInfo(); - } - JsonbAdapter newAdapter = instance != null - ? instance - : jsonbContext.getComponentInstanceCreator().getOrCreateComponent(adapterClass); - return new AdapterBinding(adaptFromType, adaptToType, newAdapter); + > AdapterBinding introspectAdapterBinding(Class adapterClass, A instance) { + return introspectBinding(adapterClass, instance, JsonbAdapter.class, ComponentBindings::getAdapterBinding, + (typeArgs, adapter) -> new AdapterBinding<>(resolveTypeArg(typeArgs[0], adapterClass), resolveTypeArg(typeArgs[1], adapterClass), adapter)); } /** @@ -306,21 +328,9 @@ AdapterBinding introspectAdapterBinding(Class adapterCla * @param instance instance to use if not cached already * @return wrapper used in property models */ - @SuppressWarnings("unchecked") - DeserializerBinding introspectDeserializerBinding(Class deserializerClass, - JsonbDeserializer instance) { - final ParameterizedType deserializerRuntimeType = ReflectionUtils - .findParameterizedType(deserializerClass, JsonbDeserializer.class); - Type deserializerBindingType = resolveTypeArg(deserializerRuntimeType.getActualTypeArguments()[0], deserializerClass); - final ComponentBindings componentBindings = getBindingInfo(deserializerBindingType); - if (componentBindings.getDeserializer() != null && componentBindings.getDeserializer().getClass() - .equals(deserializerClass)) { - return componentBindings.getDeserializer(); - } else { - JsonbDeserializer deserializer = instance != null ? instance : jsonbContext.getComponentInstanceCreator() - .getOrCreateComponent(deserializerClass); - return new DeserializerBinding(deserializerBindingType, deserializer); - } + > DeserializerBinding introspectDeserializerBinding(Class deserializerClass, D instance) { + return introspectBinding(deserializerClass, instance, JsonbDeserializer.class, ComponentBindings::getDeserializerBinding, + (typeArgs, deserializer) -> new DeserializerBinding<>(resolveTypeArg(typeArgs[0], deserializerClass), deserializer)); } /** @@ -331,20 +341,10 @@ DeserializerBinding introspectDeserializerBinding(Class serializerClass, JsonbSerializer instance) { - final ParameterizedType serializerRuntimeType = ReflectionUtils - .findParameterizedType(serializerClass, JsonbSerializer.class); - Type serBindingType = resolveTypeArg(serializerRuntimeType.getActualTypeArguments()[0], serializerClass); - final ComponentBindings componentBindings = getBindingInfo(serBindingType); - if (componentBindings.getSerializer() != null && componentBindings.getSerializer().getClass().equals(serializerClass)) { - return componentBindings.getSerializer(); - } else { - JsonbSerializer serializer = instance != null ? instance : jsonbContext.getComponentInstanceCreator() - .getOrCreateComponent(serializerClass); - return new SerializerBinding(serBindingType, serializer); - } + > SerializerBinding introspectSerializerBinding(Class serializerClass, S instance) { + return introspectBinding(serializerClass, instance, JsonbSerializer.class, ComponentBindings::getSerializerBinding, + (typeArgs, serializer) -> new SerializerBinding<>(resolveTypeArg(typeArgs[0], serializerClass), serializer)); } private Type resolveTypeArg(Type adapterTypeArg, Type adapterType) { @@ -353,7 +353,7 @@ private Type resolveTypeArg(Type adapterTypeArg, Type adapterType) { } else if (adapterTypeArg instanceof TypeVariable) { LinkedList chain = new LinkedList<>(); chain.add(adapterType); - return ReflectionUtils.resolveItemVariableType(chain, (TypeVariable) adapterTypeArg, true); + return ReflectionUtils.resolveItemVariableType(chain, (TypeVariable) adapterTypeArg/*, true*/); } else { return adapterTypeArg; } diff --git a/src/main/java/org/eclipse/yasson/internal/ConstructorPropertiesAnnotationIntrospector.java b/src/main/java/org/eclipse/yasson/internal/ConstructorPropertiesAnnotationIntrospector.java index 7d3fe87db..4e524e7f8 100644 --- a/src/main/java/org/eclipse/yasson/internal/ConstructorPropertiesAnnotationIntrospector.java +++ b/src/main/java/org/eclipse/yasson/internal/ConstructorPropertiesAnnotationIntrospector.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024 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 @@ -83,7 +83,7 @@ private JsonbCreator createJsonbCreator(Executable executable, String[] properti CreatorModel[] creatorModels = new CreatorModel[parameters.length]; for (int i = 0; i < parameters.length; i++) { final Parameter parameter = parameters[i]; - creatorModels[i] = new CreatorModel(properties[i], parameter, executable, jsonbContext); + creatorModels[i] = new CreatorModel(properties[i], parameter, /*executable,*/ jsonbContext); } return new JsonbCreator(executable, creatorModels); } diff --git a/src/main/java/org/eclipse/yasson/internal/JsonBinding.java b/src/main/java/org/eclipse/yasson/internal/JsonBinding.java index cd4764a3a..c28980e68 100644 --- a/src/main/java/org/eclipse/yasson/internal/JsonBinding.java +++ b/src/main/java/org/eclipse/yasson/internal/JsonBinding.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024 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 @@ -12,6 +12,7 @@ package org.eclipse.yasson.internal; +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; @@ -21,6 +22,7 @@ import java.lang.reflect.Type; import java.nio.charset.Charset; import java.util.Map; +import java.util.Objects; import java.util.Set; import jakarta.json.JsonStructure; @@ -51,70 +53,51 @@ public class JsonBinding implements YassonJsonb { } } - private T deserialize(final Type type, final JsonParser parser, final DeserializationContextImpl unmarshaller) { - return unmarshaller.deserialize(type, parser); + private T deserialize(final Type type, final JsonParser parser) { + Objects.requireNonNull(parser); + try (parser) { + return new DeserializationContextImpl(jsonbContext).deserialize(type, parser); + } } @Override public T fromJson(String str, Class type) throws JsonbException { - try (JsonParser parser = jsonbContext.getJsonProvider().createParser(new StringReader(str))) { - final DeserializationContextImpl unmarshaller = new DeserializationContextImpl(jsonbContext); - return deserialize(type, parser, unmarshaller); - } + return deserialize(type, jsonbContext.getJsonProvider().createParser(new StringReader(str))); } @Override public T fromJson(String str, Type type) throws JsonbException { - try (JsonParser parser = jsonbContext.getJsonProvider().createParser(new StringReader(str))) { - DeserializationContextImpl unmarshaller = new DeserializationContextImpl(jsonbContext); - return deserialize(type, parser, unmarshaller); - } + return deserialize(type, jsonbContext.getJsonProvider().createParser(new StringReader(str))); } @Override public T fromJson(Reader reader, Class type) throws JsonbException { - try (JsonParser parser = jsonbContext.getJsonProvider().createParser(reader)) { - DeserializationContextImpl unmarshaller = new DeserializationContextImpl(jsonbContext); - return deserialize(type, parser, unmarshaller); - } + return deserialize(type, jsonbContext.getJsonProvider().createParser(reader)); } @Override public T fromJson(Reader reader, Type type) throws JsonbException { - try (JsonParser parser = jsonbContext.getJsonProvider().createParser(reader)) { - DeserializationContextImpl unmarshaller = new DeserializationContextImpl(jsonbContext); - return deserialize(type, parser, unmarshaller); - } + return deserialize(type, jsonbContext.getJsonProvider().createParser(reader)); } @Override public T fromJson(InputStream stream, Class clazz) throws JsonbException { - DeserializationContextImpl unmarshaller = new DeserializationContextImpl(jsonbContext); - try (JsonParser parser = inputStreamParser(stream)) { - return deserialize(clazz, inputStreamParser(stream), unmarshaller); - } + return deserialize(clazz, inputStreamParser(stream)); } @Override public T fromJson(InputStream stream, Type type) throws JsonbException { - DeserializationContextImpl unmarshaller = new DeserializationContextImpl(jsonbContext); - try (JsonParser parser = inputStreamParser(stream)) { - return deserialize(type, inputStreamParser(stream), unmarshaller); - } + return deserialize(type, inputStreamParser(stream)); } @Override public T fromJsonStructure(JsonStructure jsonStructure, Class type) throws JsonbException { - try (JsonParser parser = new JsonStructureToParserAdapter(jsonStructure)) { - return deserialize(type, parser, new DeserializationContextImpl(jsonbContext)); - } + return deserialize(type, new JsonStructureToParserAdapter(jsonStructure, jsonbContext.getJsonProvider())); } @Override public T fromJsonStructure(JsonStructure jsonStructure, Type runtimeType) throws JsonbException { - try (JsonParser parser = new JsonStructureToParserAdapter(jsonStructure)) { - return deserialize(runtimeType, parser, new DeserializationContextImpl(jsonbContext)); - } + return deserialize(runtimeType, new JsonStructureToParserAdapter(jsonStructure, jsonbContext.getJsonProvider())); } private JsonParser inputStreamParser(InputStream stream) { @@ -230,8 +213,7 @@ private JsonGenerator streamGenerator(OutputStream stream) { } @Override - public void close() throws Exception { + public void close() throws IOException { jsonbContext.getComponentInstanceCreator().close(); } - -} +} \ No newline at end of file diff --git a/src/main/java/org/eclipse/yasson/internal/JsonParserStreamCreator.java b/src/main/java/org/eclipse/yasson/internal/JsonParserStreamCreator.java new file mode 100644 index 000000000..3c29cc877 --- /dev/null +++ b/src/main/java/org/eclipse/yasson/internal/JsonParserStreamCreator.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2024 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.internal; + +import java.util.AbstractMap; +import java.util.Map; +import java.util.Objects; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import jakarta.json.JsonException; +import jakarta.json.JsonValue; +import jakarta.json.stream.JsonParser; +import jakarta.json.stream.JsonParser.Event; + +import org.eclipse.yasson.internal.properties.MessageKeys; +import org.eclipse.yasson.internal.properties.Messages; + +public class JsonParserStreamCreator { + + private final JsonParser parser; + private final boolean nextBeforeCreationOfValueStream; + private final Supplier currenEventSupplier; + private final Supplier canProduceValueStream; + + public JsonParserStreamCreator(JsonParser parser, boolean nextBeforeCreationOfValueStream, Supplier currenEventSupplier, + Supplier canProduceValueStream) { + + this.parser = Objects.requireNonNull(parser); + this.nextBeforeCreationOfValueStream = nextBeforeCreationOfValueStream; + this.currenEventSupplier = Objects.requireNonNull(currenEventSupplier); + this.canProduceValueStream = Objects.requireNonNull(canProduceValueStream); + } + + /** + * Creates new {@link Stream} from values from {@link Supplier}. The stream delivers the values as long as supplier delivers non-null values + * + * @param supplier supplier of the values + * @param type of the values which are delivered by the supplier and the stream + * @return stream of values from given supplier + */ + private static Stream streamFromSupplier(Supplier supplier) { + return Stream.iterate(Objects.requireNonNull(supplier).get(), Objects::nonNull, value -> supplier.get()); + } + + public Stream getArrayStream() { + if (currenEventSupplier.get() == Event.START_ARRAY) { + return streamFromSupplier(() -> (parser.hasNext() && parser.next() != Event.END_ARRAY) ? parser.getValue() : null); + } else { + throw new IllegalStateException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "Outside of array context")); + } + } + + public Stream> getObjectStream() { + if (currenEventSupplier.get() == Event.START_OBJECT) { + return streamFromSupplier(() -> { + if (!parser.hasNext()) { + return null; + } + Event e = parser.next(); + if (e == Event.END_OBJECT) { + return null; + } else if (e != Event.KEY_NAME) { + throw new JsonException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "Cannot read object key")); + } else { + String key = parser.getString(); + if (!parser.hasNext()) { + throw new JsonException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "Cannot read object value")); + } else { + parser.next(); + return new AbstractMap.SimpleImmutableEntry<>(key, parser.getValue()); + } + } + }); + } else { + throw new IllegalStateException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "Outside of object context")); + } + } + + public Stream getValueStream() { + if (canProduceValueStream.get()) { + if (nextBeforeCreationOfValueStream) { + parser.next(); + } + + return streamFromSupplier(() -> { + if (parser.hasNext()) { + return parser.getValue(); + } else { + return null; + } + }); + } else { + throw new IllegalStateException( + Messages.getMessage(MessageKeys.INTERNAL_ERROR, "getValueStream can be only called at the root level of JSON structure")); + } + } +} diff --git a/src/main/java/org/eclipse/yasson/internal/JsonbContext.java b/src/main/java/org/eclipse/yasson/internal/JsonbContext.java index 721e67e72..537668359 100644 --- a/src/main/java/org/eclipse/yasson/internal/JsonbContext.java +++ b/src/main/java/org/eclipse/yasson/internal/JsonbContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024 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 @@ -177,18 +177,12 @@ private JsonParserFactory initJsonParserFactory() { protected Map createJsonpProperties(JsonbConfig jsonbConfig) { //JSONP 1.0 actually ignores the value, just checks the key is present. Only set if JsonbConfig.FORMATTING is true. final Optional property = jsonbConfig.getProperty(JsonbConfig.FORMATTING); - final Map factoryProperties = new HashMap<>(); - if (property.isPresent()) { - final Object value = property.get(); + return property.map(value -> { if (!(value instanceof Boolean)) { throw new JsonbException(Messages.getMessage(MessageKeys.JSONB_CONFIG_FORMATTING_ILLEGAL_VALUE)); } - if ((Boolean) value) { - factoryProperties.put(JsonGenerator.PRETTY_PRINTING, Boolean.TRUE); - } - return factoryProperties; - } - return factoryProperties; + return (Boolean) value ? new HashMap<>(Map.of(JsonGenerator.PRETTY_PRINTING, Boolean.TRUE)) : new HashMap(); + }).orElse(new HashMap<>()); } private JsonbComponentInstanceCreator initComponentInstanceCreator() { diff --git a/src/main/java/org/eclipse/yasson/internal/MappingContext.java b/src/main/java/org/eclipse/yasson/internal/MappingContext.java index 9a91c2543..a5608fd58 100644 --- a/src/main/java/org/eclipse/yasson/internal/MappingContext.java +++ b/src/main/java/org/eclipse/yasson/internal/MappingContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024 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 @@ -25,7 +25,7 @@ /** * JSONB mappingContext. Created once per {@link jakarta.json.bind.Jsonb} instance. Represents a global scope. * Holds internal model. - * + *

* Thread safe. */ public class MappingContext { @@ -90,13 +90,13 @@ private static Function, ClassModel> createParseClassModelFunction(Clas ? ClassCustomization.empty() : parentClassModel.getClassCustomization(), jsonbContext.getConfigProperties().getPropertyNamingStrategy()); - // PolymorphismSupport configPolymorphism = jsonbContext.getConfigProperties().getPolymorphismSupport(); -// if (configPolymorphism != null) { -// customization = mergeConfigAndAnnotationPolymorphism(configPolymorphism, -// configPolymorphism.getClassPolymorphism(aClass), -// customization, -// aClass); -// } + /*PolymorphismSupport configPolymorphism = jsonbContext.getConfigProperties().getPolymorphismSupport(); + if (configPolymorphism != null) { + customization = mergeConfigAndAnnotationPolymorphism(configPolymorphism, + configPolymorphism.getClassPolymorphism(aClass), + customization, + aClass); + }*/ ClassModel newClassModel = new ClassModel(aClass, customization, parentClassModel, @@ -108,39 +108,39 @@ private static Function, ClassModel> createParseClassModelFunction(Clas }; } -// private static ClassCustomization mergeConfigAndAnnotationPolymorphism(PolymorphismSupport generalPolymorphism, -// Optional maybeClassPolymorphism, -// ClassCustomization customization, -// Class aClass) { -// PolymorphismConfig polymorphismConfig = customization.getPolymorphismConfig(); -// PolymorphismConfig.Builder polyConfigBuilder; -// if (polymorphismConfig != null) { -// polyConfigBuilder = PolymorphismConfig.builder().of(polymorphismConfig); -// } else { -// polyConfigBuilder = PolymorphismConfig.builder(); -// maybeClassPolymorphism.ifPresent(classPolymorphism -> polyConfigBuilder -// .inherited(!classPolymorphism.getBoundClass().equals(aClass))); -// } -// generalPolymorphism.getKeyName().filter(s -> !s.isEmpty()).ifPresent(polyConfigBuilder::fieldName); -// generalPolymorphism.useClassNames().ifPresent(polyConfigBuilder::useClassNames); -// polyConfigBuilder.whitelistedPackages(generalPolymorphism.getWhitelistedPackages()); -// -// maybeClassPolymorphism.ifPresent(classPolymorphism -> { -// classPolymorphism.getKeyName().filter(s -> !s.isEmpty()).ifPresent(polyConfigBuilder::fieldName); -// classPolymorphism.useClassNames().ifPresent(polyConfigBuilder::useClassNames); -// classPolymorphism.getFormat().ifPresent(polyConfigBuilder::format); -// classPolymorphism.getAliases().forEach(polyConfigBuilder::alias); -// polyConfigBuilder.whitelistedPackages(classPolymorphism.getWhitelistedPackages()); -// }); -// PolymorphismConfig polyConfigMerged = polyConfigBuilder.build(); -// if (polyConfigMerged.getFieldName() == null || polyConfigMerged.getFieldName().isEmpty()) { -// throw new JsonbException("Polymorphism type field name cannot be null or empty: " + aClass); -// } -// return ClassCustomization.builder() -// .of(customization) -// .polymorphismConfig(polyConfigMerged) -// .build(); -// } + /*private static ClassCustomization mergeConfigAndAnnotationPolymorphism(PolymorphismSupport generalPolymorphism, + Optional maybeClassPolymorphism, + ClassCustomization customization, + Class aClass) { + PolymorphismConfig polymorphismConfig = customization.getPolymorphismConfig(); + PolymorphismConfig.Builder polyConfigBuilder; + if (polymorphismConfig != null) { + polyConfigBuilder = PolymorphismConfig.builder().of(polymorphismConfig); + } else { + polyConfigBuilder = PolymorphismConfig.builder(); + maybeClassPolymorphism.ifPresent(classPolymorphism -> polyConfigBuilder + .inherited(!classPolymorphism.getBoundClass().equals(aClass))); + } + generalPolymorphism.getKeyName().filter(s -> !s.isEmpty()).ifPresent(polyConfigBuilder::fieldName); + generalPolymorphism.useClassNames().ifPresent(polyConfigBuilder::useClassNames); + polyConfigBuilder.whitelistedPackages(generalPolymorphism.getWhitelistedPackages()); + + maybeClassPolymorphism.ifPresent(classPolymorphism -> { + classPolymorphism.getKeyName().filter(s -> !s.isEmpty()).ifPresent(polyConfigBuilder::fieldName); + classPolymorphism.useClassNames().ifPresent(polyConfigBuilder::useClassNames); + classPolymorphism.getFormat().ifPresent(polyConfigBuilder::format); + classPolymorphism.getAliases().forEach(polyConfigBuilder::alias); + polyConfigBuilder.whitelistedPackages(classPolymorphism.getWhitelistedPackages()); + }); + PolymorphismConfig polyConfigMerged = polyConfigBuilder.build(); + if (polyConfigMerged.getFieldName() == null || polyConfigMerged.getFieldName().isEmpty()) { + throw new JsonbException("Polymorphism type field name cannot be null or empty: " + aClass); + } + return ClassCustomization.builder() + .of(customization) + .polymorphismConfig(polyConfigMerged) + .build(); + }*/ /** * Search for class model, without parsing if not found. diff --git a/src/main/java/org/eclipse/yasson/internal/ReflectionUtils.java b/src/main/java/org/eclipse/yasson/internal/ReflectionUtils.java index 66ccac94e..1e465ecb3 100644 --- a/src/main/java/org/eclipse/yasson/internal/ReflectionUtils.java +++ b/src/main/java/org/eclipse/yasson/internal/ReflectionUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024 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 @@ -26,7 +26,6 @@ import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.logging.Logger; import jakarta.json.bind.JsonbException; @@ -38,7 +37,7 @@ */ public class ReflectionUtils { - private static final Logger LOGGER = Logger.getLogger(ReflectionUtils.class.getName()); + /*private static final Logger LOGGER = Logger.getLogger(ReflectionUtils.class.getName());*/ private ReflectionUtils() { throw new IllegalStateException("Utility classes should not be instantiated."); @@ -47,7 +46,6 @@ private ReflectionUtils() { /** * Get raw type by type. * Only for ParametrizedTypes, GenericArrayTypes and Classes. - * * Empty optional is returned if raw type cannot be resolved. * * @param type Type to get class information from, not null. @@ -60,19 +58,17 @@ public static Optional> getOptionalRawType(Type type) { return Optional.of((Class) ((ParameterizedType) type).getRawType()); } else if (type instanceof GenericArrayType) { return Optional.of(((GenericArrayType) type).getClass()); + } else if (type instanceof WildcardType) { + return Optional.of((Class) ((WildcardType) type).getUpperBounds()[0]); } else if (type instanceof TypeVariable) { TypeVariable typeVariable = TypeVariable.class.cast(type); if (Objects.nonNull(typeVariable.getBounds())) { - Optional> specializedClass = Optional.empty(); - for (Type bound : typeVariable.getBounds()) { - Optional> boundRawType = getOptionalRawType(bound); - if (boundRawType.isPresent() && !Object.class.equals(boundRawType.get())) { - if (!specializedClass.isPresent() || specializedClass.get().isAssignableFrom(boundRawType.get())) { - specializedClass = Optional.of(boundRawType.get()); - } - } - } - return specializedClass; + return Arrays.stream(typeVariable.getBounds()) + .map(ReflectionUtils::getOptionalRawType) + .filter(Optional::isPresent) + .filter(clazz -> !Object.class.equals(clazz.get())) + .reduce((clazz1, clazz2) -> clazz1.map(clazzGet1 -> clazzGet1.isAssignableFrom(clazz2.get())).orElse(false) ? clazz2 : clazz1) + .orElse(Optional.empty()); } } return Optional.empty(); @@ -81,7 +77,7 @@ public static Optional> getOptionalRawType(Type type) { /** * Get raw type by type. * Resolves only ParametrizedTypes, GenericArrayTypes and Classes. - * + *

* Exception is thrown if raw type cannot be resolved. * * @param type Type to get class information from, not null. @@ -92,7 +88,7 @@ public static Class getRawType(Type type) { .orElseThrow(() -> new JsonbException(Messages.getMessage(MessageKeys.TYPE_RESOLUTION_ERROR, type))); } - /** + /*/** * Get a raw type of any type. * If type is a {@link TypeVariable} recursively search type chain for resolution of typevar. * If type is a {@link WildcardType} find most specific upper / lower bound, which can be used. If most specific @@ -102,7 +98,7 @@ public static Class getRawType(Type type) { * @param type type to resolve, typically field type or generic bound, not null. * @return resolved raw class */ - public static Class resolveRawType(List chain, Type type) { + /*public static Class resolveRawType(List chain, Type type) { if (type instanceof Class) { return (Class) type; } else if (type instanceof ParameterizedType) { @@ -110,7 +106,7 @@ public static Class resolveRawType(List chain, Type type) { } else { return getRawType(resolveType(chain, type)); } - } + }*/ /** * Resolve a type by chain. @@ -136,27 +132,27 @@ private static Type resolveType(List chain, Type type, boolean warn) { if (toResolve instanceof WildcardType) { return resolveMostSpecificBound(chain, (WildcardType) toResolve, warn); } else if (toResolve instanceof TypeVariable) { - return resolveItemVariableType(chain, (TypeVariable) toResolve, warn); + return resolveItemVariableType(chain, (TypeVariable) toResolve/*, warn*/); } else if (toResolve instanceof ParameterizedType) { return resolveTypeArguments((ParameterizedType) toResolve, chain.get(chain.size() - 1)); } return type; } - /** + /*/** * Resolves type by item information and wraps it with {@link Optional}. * * @param chain hierarchy of all wrapping types. * @param type type * @return resolved type wrapped with Optional */ - public static Optional resolveOptionalType(List chain, Type type) { + /*public static Optional resolveOptionalType(List chain, Type type) { try { return Optional.of(resolveType(chain, type, false)); } catch (RuntimeException e) { return Optional.empty(); } - } + }*/ /** * Resolve a bounded type variable type by its wrapper types. @@ -165,24 +161,24 @@ public static Optional resolveOptionalType(List chain, Type type) { * * @param chain chain to search "runtime" generic type of a TypeVariable. * @param typeVariable type to search in chain for, not null. - * @param warn whether or not to log a warning message when bounds are not found +// * @param warn whether or not to log a warning message when bounds are not found * @return Type of a generic "runtime" bound, not null. */ - public static Type resolveItemVariableType(List chain, TypeVariable typeVariable, boolean warn) { -// if (chain == null) { -// Optional> optionalRawType = getOptionalRawType(typeVariable); -// if (optionalRawType.isPresent()) { -// return optionalRawType.get(); -// } - - // //Bound not found, treat it as an Object.class -// if (warn) { -// LOGGER.warning(Messages.getMessage(MessageKeys.GENERIC_BOUND_NOT_FOUND, -// typeVariable, -// typeVariable.getGenericDeclaration())); -// } -// return Object.class; -// } + public static Type resolveItemVariableType(List chain, TypeVariable typeVariable/*, boolean warn*/) { + /*if (chain == null) { + Optional> optionalRawType = getOptionalRawType(typeVariable); + if (optionalRawType.isPresent()) { + return optionalRawType.get(); + } + + //Bound not found, treat it as an Object.class + if (warn) { + LOGGER.warning(Messages.getMessage(MessageKeys.GENERIC_BOUND_NOT_FOUND, + typeVariable, + typeVariable.getGenericDeclaration())); + } + return Object.class; + }*/ Type returnType = typeVariable; for (int i = chain.size() - 1; i >= 0; i--) { Type type = chain.get(i); @@ -200,23 +196,23 @@ public static Type resolveItemVariableType(List chain, TypeVariable typ } return returnType; -// //Embedded items doesn't hold information about variable types -// if (chain instanceof EmbeddedItem) { -// return resolveItemVariableType(chain.getWrapper(), typeVariable, warn); -// } -// -// ParameterizedType wrapperParameterizedType = findParameterizedSuperclass(chain.getRuntimeType()); -// -// VariableTypeInheritanceSearch search = new VariableTypeInheritanceSearch(); -// Type foundType = search.searchParametrizedType(wrapperParameterizedType, typeVariable); -// if (foundType != null) { -// if (foundType instanceof TypeVariable) { -// return resolveItemVariableType(chain.getWrapper(), (TypeVariable) foundType, warn); -// } -// return foundType; -// } -// -// return resolveItemVariableType(chain.getWrapper(), typeVariable, warn); + //Embedded items doesn't hold information about variable types + /*if (chain instanceof EmbeddedItem) { + return resolveItemVariableType(chain.getWrapper(), typeVariable, warn); + } + + ParameterizedType wrapperParameterizedType = findParameterizedSuperclass(chain.getRuntimeType()); + + VariableTypeInheritanceSearch search = new VariableTypeInheritanceSearch(); + Type foundType = search.searchParametrizedType(wrapperParameterizedType, typeVariable); + if (foundType != null) { + if (foundType instanceof TypeVariable) { + return resolveItemVariableType(chain.getWrapper(), (TypeVariable) foundType, warn); + } + return foundType; + } + + return resolveItemVariableType(chain.getWrapper(), typeVariable, warn);*/ } /** @@ -328,7 +324,7 @@ public static Constructor getDefaultConstructor(Class clazz, boolean r * @return type of JsonbAdapter */ public static ParameterizedType findParameterizedType(Class classToSearch, Class parameterizedInterface) { - Class current = classToSearch; + Class current = classToSearch; while (current != Object.class) { for (Type currentInterface : current.getGenericInterfaces()) { if (currentInterface instanceof ParameterizedType @@ -361,15 +357,15 @@ public static boolean isResolvedType(Type type) { return type instanceof Class; } - private static ParameterizedType findParameterizedSuperclass(Type type) { + /*private static ParameterizedType findParameterizedSuperclass(Type type) { if (type == null || type instanceof ParameterizedType) { return (ParameterizedType) type; } if (!(type instanceof Class)) { throw new JsonbException("Can't resolve ParameterizedType superclass for: " + type); } - return findParameterizedSuperclass(((Class) type).getGenericSuperclass()); - } + return findParameterizedSuperclass(((Class) type).getGenericSuperclass()); + }*/ /** * Resolves a wildcard most specific upper or lower bound. @@ -431,7 +427,6 @@ public String toString() { public boolean equals(Object o) { if (o instanceof GenericArrayType) { GenericArrayType that = (GenericArrayType) o; - return Objects.equals(genericComponentType, that.getGenericComponentType()); } else { return false; diff --git a/src/main/java/org/eclipse/yasson/internal/VariableTypeInheritanceSearch.java b/src/main/java/org/eclipse/yasson/internal/VariableTypeInheritanceSearch.java index 263cc4e21..0ceefca7d 100644 --- a/src/main/java/org/eclipse/yasson/internal/VariableTypeInheritanceSearch.java +++ b/src/main/java/org/eclipse/yasson/internal/VariableTypeInheritanceSearch.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024 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 @@ -83,11 +83,11 @@ Type searchParametrizedType(Type typeToSearch, TypeVariable typeVar) { return matchedGenericType; } parameterizedSubclasses.push(parameterizedType); - return searchParametrizedType(((Class) parameterizedType.getRawType()).getGenericSuperclass(), typeVar); + return searchParametrizedType(((Class) parameterizedType.getRawType()).getGenericSuperclass(), typeVar); } - private Type checkSubclassRuntimeInfo(TypeVariable typeVar) { - if (parameterizedSubclasses.size() == 0) { + private Type checkSubclassRuntimeInfo(TypeVariable typeVar) { + if (parameterizedSubclasses.isEmpty()) { return typeVar; } ParameterizedType parametrizedSubclass = parameterizedSubclasses.pop(); @@ -98,17 +98,21 @@ private Type searchRuntimeTypeArgument(ParameterizedType runtimeType, TypeVariab if (ReflectionUtils.getRawType(runtimeType) != typeVar.getGenericDeclaration()) { return null; } - TypeVariable[] bounds = typeVar.getGenericDeclaration().getTypeParameters(); - for (int i = 0; i < bounds.length; i++) { - if (bounds[i].equals(typeVar)) { - Type matchedGenericType = runtimeType.getActualTypeArguments()[i]; + TypeVariable[] bounds = typeVar.getGenericDeclaration().getTypeParameters(); + Type[] actualTypeArguments = runtimeType.getActualTypeArguments(); + int i = 0; + + for (TypeVariable bound : bounds) { + if (bound.equals(typeVar)) { + Type matchedGenericType = actualTypeArguments[i]; //Propagated generic types to another generic classes if (matchedGenericType instanceof TypeVariable) { - return checkSubclassRuntimeInfo((TypeVariable) matchedGenericType); + return checkSubclassRuntimeInfo((TypeVariable) matchedGenericType); } //found runtime matchedGenericType return matchedGenericType; } + i++; } return null; } @@ -120,6 +124,6 @@ private static ParameterizedType findParameterizedSuperclass(Type type) { if (!(type instanceof Class)) { throw new JsonbException(Messages.getMessage(MessageKeys.RESOLVE_PARAMETRIZED_TYPE, type)); } - return findParameterizedSuperclass(((Class) type).getGenericSuperclass()); + return findParameterizedSuperclass(((Class) type).getGenericSuperclass()); } } diff --git a/src/main/java/org/eclipse/yasson/internal/components/AbstractComponentBinding.java b/src/main/java/org/eclipse/yasson/internal/components/AbstractComponentBinding.java index f3be5887b..380c31fff 100644 --- a/src/main/java/org/eclipse/yasson/internal/components/AbstractComponentBinding.java +++ b/src/main/java/org/eclipse/yasson/internal/components/AbstractComponentBinding.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024 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 @@ -17,24 +17,31 @@ /** * Wrapper for user components, components, (de)serializer. - * Contains resolved binding type an component. + * Contains resolved binding type and component. + * + * @param type of component */ -public abstract class AbstractComponentBinding { +public abstract class AbstractComponentBinding { private final Type bindingType; + private final C component; + /** * Creates info. * * @param bindingType type to which component is bound. + * @param component bound component. */ - public AbstractComponentBinding(Type bindingType) { + public AbstractComponentBinding(Type bindingType, C component) { Objects.requireNonNull(bindingType); + Objects.requireNonNull(component); this.bindingType = bindingType; + this.component = component; } /** - * Resolved binding type of a component. + * Resolved binding type of the component. * * @return binding type */ @@ -42,10 +49,21 @@ public Type getBindingType() { return bindingType; } + /** + * Get actual user component. + * + * @return user component. + */ + public C getComponent(){ + return component; + } + /** * Class of user component. * * @return component class */ - public abstract Class getComponentClass(); + public Class getComponentClass(){ + return component.getClass(); + } } diff --git a/src/main/java/org/eclipse/yasson/internal/components/AdapterBinding.java b/src/main/java/org/eclipse/yasson/internal/components/AdapterBinding.java index 16c57d7ae..3b8fbe785 100644 --- a/src/main/java/org/eclipse/yasson/internal/components/AdapterBinding.java +++ b/src/main/java/org/eclipse/yasson/internal/components/AdapterBinding.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024 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 @@ -19,13 +19,14 @@ /** * Wrapper for JsonbAdapter generic information and an components itself. + * + * @param The type for the @{@link JsonbAdapter} that JSONB doesn't know how to handle. + * @param The type for the @{@link JsonbAdapter} that JSONB knows how to handle out of the box. */ -public class AdapterBinding extends AbstractComponentBinding { +public class AdapterBinding extends AbstractComponentBinding> { private final Type toType; - private final JsonbAdapter adapter; - /** * Adapter info with type to "adapt from", type to "adapt to" and an components itself. * @@ -33,17 +34,15 @@ public class AdapterBinding extends AbstractComponentBinding { * @param toType to not null * @param adapter components not null */ - public AdapterBinding(Type fromType, Type toType, JsonbAdapter adapter) { - super(fromType); + public AdapterBinding(Type fromType, Type toType, JsonbAdapter adapter) { + super(fromType, adapter); Objects.requireNonNull(toType); - Objects.requireNonNull(adapter); this.toType = toType; - this.adapter = adapter; } /** * Represents a type to which to adapt into. - * + *

* During marshalling object property is adapted to this type and result is marshalled. * During unmarshalling object is unmarshalled into this type first, than converted to field type and set. * @@ -52,18 +51,4 @@ public AdapterBinding(Type fromType, Type toType, JsonbAdapter adapter) { public Type getToType() { return toType; } - - /** - * Get actual components to adapt object value. - * - * @return components - */ - public JsonbAdapter getAdapter() { - return adapter; - } - - @Override - public Class getComponentClass() { - return adapter.getClass(); - } } diff --git a/src/main/java/org/eclipse/yasson/internal/components/BeanManagerInstanceCreator.java b/src/main/java/org/eclipse/yasson/internal/components/BeanManagerInstanceCreator.java index 456015a06..3e6d542ff 100644 --- a/src/main/java/org/eclipse/yasson/internal/components/BeanManagerInstanceCreator.java +++ b/src/main/java/org/eclipse/yasson/internal/components/BeanManagerInstanceCreator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024 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 @@ -31,7 +31,7 @@ * CDI instance manager. * Instances are created and stored per instance of {@link JsonBinding}. * Calling close on JsonBinding, cleans up Jsonb CDI instances and in case of "dependant" scope its dependencies. - * + *

* CDI API dependency is optional, this class is never referenced / loaded if CDI API is not resolvable. */ public class BeanManagerInstanceCreator implements JsonbComponentInstanceCreator { @@ -61,9 +61,9 @@ public BeanManagerInstanceCreator(Object beanManager) { * @return New instance of bean class with injected content. */ @Override - @SuppressWarnings("unchecked") public T getOrCreateComponent(Class componentClass) { - return (T) injectionTargets.computeIfAbsent(componentClass, clazz -> { + @SuppressWarnings("unchecked") + T instance = (T) injectionTargets.computeIfAbsent(componentClass, clazz -> { final AnnotatedType aType = beanManager.createAnnotatedType(componentClass); final InjectionTarget injectionTarget = beanManager.getInjectionTargetFactory(aType) .createInjectionTarget(null); @@ -71,8 +71,9 @@ public T getOrCreateComponent(Class componentClass) { final T beanInstance = injectionTarget.produce(creationalContext); injectionTarget.inject(beanInstance, creationalContext); injectionTarget.postConstruct(beanInstance); - return new CDIManagedBean(beanInstance, injectionTarget, creationalContext); + return new CDIManagedBean<>(beanInstance, injectionTarget, creationalContext); }).getInstance(); + return instance; } @Override @@ -82,9 +83,16 @@ public void close() throws IOException { } private void cleanupBean(CDIManagedBean bean) { - bean.getInjectionTarget().preDestroy(bean.getInstance()); - bean.getInjectionTarget().dispose(bean.getInstance()); - bean.getCreationalContext().release(); + InjectionTarget injectionTarget = bean.getInjectionTarget(); + if (injectionTarget != null) { + injectionTarget.preDestroy(bean.getInstance()); + injectionTarget.dispose(bean.getInstance()); + } + + CreationalContext creationalContext = bean.getCreationalContext(); + if (creationalContext != null) { + creationalContext.release(); + } } /** diff --git a/src/main/java/org/eclipse/yasson/internal/components/ComponentBindings.java b/src/main/java/org/eclipse/yasson/internal/components/ComponentBindings.java index 3033bd5b0..020777d9c 100644 --- a/src/main/java/org/eclipse/yasson/internal/components/ComponentBindings.java +++ b/src/main/java/org/eclipse/yasson/internal/components/ComponentBindings.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024 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 @@ -13,19 +13,28 @@ package org.eclipse.yasson.internal.components; import java.lang.reflect.Type; +import java.util.Objects; + +import jakarta.json.bind.adapter.JsonbAdapter; +import jakarta.json.bind.serializer.JsonbDeserializer; +import jakarta.json.bind.serializer.JsonbSerializer; /** * Wrapper holding singleton instances of user defined components - Adapters, (De)Serializers. + * + * @param The type for the @{@link JsonbAdapter} that JSONB doesn't know how to handle. + * Also type for the @{@link JsonbSerializer} to serialize and for the @{@link JsonbDeserializer} to deserialize. + * @param The type for @{@link JsonbAdapter} that JSONB knows how to handle out of the box. */ -public class ComponentBindings { +public class ComponentBindings { private final Type bindingType; - private final SerializerBinding serializer; + private final SerializerBinding serializerBinding; - private final DeserializerBinding deserializer; + private final DeserializerBinding deserializerBinding; - private final AdapterBinding adapterInfo; + private final AdapterBinding adapterBinding; /** * Construct empty bindings for a given type. @@ -39,19 +48,53 @@ public ComponentBindings(Type bindingType) { /** * Creates an instance and populates it with bindings for a given type. * - * @param bindingType Type components are bound to. - * @param serializer Serializer. - * @param deserializer Deserializer. - * @param adapter Adapter. + * @param bindingType Type components are bound to. + * @param serializerBinding Serializer. + * @param deserializerBinding Deserializer. + * @param adapterBinding Adapter. */ - public ComponentBindings(Type bindingType, - SerializerBinding serializer, - DeserializerBinding deserializer, - AdapterBinding adapter) { + private ComponentBindings(Type bindingType, + SerializerBinding serializerBinding, + DeserializerBinding deserializerBinding, + AdapterBinding adapterBinding) { + Objects.requireNonNull(bindingType); this.bindingType = bindingType; - this.serializer = serializer; - this.deserializer = deserializer; - this.adapterInfo = adapter; + this.serializerBinding = serializerBinding; + this.deserializerBinding = deserializerBinding; + this.adapterBinding = adapterBinding; + } + + /** + * Creates a copy of the given bindings and new serializer. + * + * @param bindings Deserializer and adapter will be copied from this instance. + * @param serializerBinding New serializer. The bound type for the copy will be also taken from this serializer. + */ + public ComponentBindings(ComponentBindings bindings, + SerializerBinding serializerBinding) { + this(Objects.requireNonNull(serializerBinding).getBindingType(), serializerBinding, bindings.deserializerBinding, bindings.adapterBinding); + } + + /** + * Creates a copy of the given bindings and new deserializer. + * + * @param bindings Serializer and adapter will be copied from this instance. + * @param deserializerBinding New deserializer. The bound type for the copy will be also taken from this deserializer. + */ + public ComponentBindings(ComponentBindings bindings, + DeserializerBinding deserializerBinding) { + this(Objects.requireNonNull(deserializerBinding).getBindingType(), bindings.serializerBinding, deserializerBinding, bindings.adapterBinding); + } + + /** + * Creates a copy of the given bindings and new adapter. + * + * @param bindings Serializer and serializer will be copied from this instance. + * @param adapterBinding New adapter. The bound type for the copy will be also taken from this adapter. + */ + public ComponentBindings(ComponentBindings bindings, + AdapterBinding adapterBinding) { + this(Objects.requireNonNull(adapterBinding).getBindingType(), bindings.serializerBinding, bindings.deserializerBinding, adapterBinding); } /** @@ -68,8 +111,8 @@ public Type getBindingType() { * * @return serializer */ - public SerializerBinding getSerializer() { - return serializer; + public SerializerBinding getSerializerBinding() { + return serializerBinding; } /** @@ -77,8 +120,8 @@ public SerializerBinding getSerializer() { * * @return deserializer */ - public DeserializerBinding getDeserializer() { - return deserializer; + public DeserializerBinding getDeserializerBinding() { + return deserializerBinding; } /** @@ -86,8 +129,8 @@ public DeserializerBinding getDeserializer() { * * @return adapterInfo */ - public AdapterBinding getAdapterInfo() { - return adapterInfo; + public AdapterBinding getAdapterBinding() { + return adapterBinding; } } diff --git a/src/main/java/org/eclipse/yasson/internal/components/DeserializerBinding.java b/src/main/java/org/eclipse/yasson/internal/components/DeserializerBinding.java index b2ed21bb4..0518530ef 100644 --- a/src/main/java/org/eclipse/yasson/internal/components/DeserializerBinding.java +++ b/src/main/java/org/eclipse/yasson/internal/components/DeserializerBinding.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024 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 @@ -21,9 +21,7 @@ * * @param type of contained deserializer */ -public class DeserializerBinding extends AbstractComponentBinding { - - private final JsonbDeserializer jsonbDeserializer; +public class DeserializerBinding extends AbstractComponentBinding> { /** * Creates a new instance. @@ -32,21 +30,6 @@ public class DeserializerBinding extends AbstractComponentBinding { * @param jsonbDeserializer Deserializer. */ public DeserializerBinding(Type bindingType, JsonbDeserializer jsonbDeserializer) { - super(bindingType); - this.jsonbDeserializer = jsonbDeserializer; - } - - /** - * Gets deserializer if any. - * - * @return Deserializer. - */ - public JsonbDeserializer getJsonbDeserializer() { - return jsonbDeserializer; - } - - @Override - public Class getComponentClass() { - return jsonbDeserializer.getClass(); + super(bindingType, jsonbDeserializer); } } diff --git a/src/main/java/org/eclipse/yasson/internal/components/SerializerBinding.java b/src/main/java/org/eclipse/yasson/internal/components/SerializerBinding.java index 6baafde34..19fe3778e 100644 --- a/src/main/java/org/eclipse/yasson/internal/components/SerializerBinding.java +++ b/src/main/java/org/eclipse/yasson/internal/components/SerializerBinding.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024 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 @@ -21,9 +21,7 @@ * * @param type of jsonb serializer */ -public class SerializerBinding extends AbstractComponentBinding { - - private final JsonbSerializer jsonbSerializer; +public class SerializerBinding extends AbstractComponentBinding> { /** * Creates a new instance. @@ -32,26 +30,6 @@ public class SerializerBinding extends AbstractComponentBinding { * @param jsonbSerializer Serializer. Can be null. */ public SerializerBinding(Type bindingType, JsonbSerializer jsonbSerializer) { - super(bindingType); - this.jsonbSerializer = jsonbSerializer; - } - - /** - * Returns a serializer if any. - * - * @return Serializer. - */ - public JsonbSerializer getJsonbSerializer() { - return jsonbSerializer; - } - - /** - * Class of user component. - * - * @return Component class. - */ - @Override - public Class getComponentClass() { - return jsonbSerializer.getClass(); + super(bindingType, jsonbSerializer); } } diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/AdapterDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/AdapterDeserializer.java index b54e72ab7..9c97ec2bf 100644 --- a/src/main/java/org/eclipse/yasson/internal/deserializer/AdapterDeserializer.java +++ b/src/main/java/org/eclipse/yasson/internal/deserializer/AdapterDeserializer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024 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 @@ -26,14 +26,14 @@ class AdapterDeserializer implements ModelDeserializer { private final JsonbAdapter adapter; - private final AdapterBinding adapterBinding; + private final AdapterBinding adapterBinding; private final ModelDeserializer delegate; @SuppressWarnings("unchecked") - AdapterDeserializer(AdapterBinding adapterBinding, + AdapterDeserializer(AdapterBinding adapterBinding, ModelDeserializer delegate) { this.adapterBinding = adapterBinding; - this.adapter = (JsonbAdapter) adapterBinding.getAdapter(); + this.adapter = (JsonbAdapter) adapterBinding.getComponent(); this.delegate = delegate; } @@ -45,7 +45,7 @@ public Object deserialize(Object value, DeserializationContextImpl context) { throw new JsonbException(Messages.getMessage(MessageKeys.ADAPTER_EXCEPTION, adapterBinding.getBindingType(), adapterBinding.getToType(), - adapterBinding.getAdapter().getClass()), e); + adapterBinding.getComponentClass()), e); } } diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/DeserializationModelCreator.java b/src/main/java/org/eclipse/yasson/internal/deserializer/DeserializationModelCreator.java index 0ac3f980a..240d41120 100644 --- a/src/main/java/org/eclipse/yasson/internal/deserializer/DeserializationModelCreator.java +++ b/src/main/java/org/eclipse/yasson/internal/deserializer/DeserializationModelCreator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024 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 @@ -20,7 +20,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -32,6 +31,7 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; +import java.util.function.Supplier; import java.util.stream.Collectors; import jakarta.json.bind.JsonbException; @@ -39,6 +39,7 @@ import jakarta.json.bind.config.PropertyNamingStrategy; import jakarta.json.stream.JsonParser; +import org.eclipse.yasson.internal.ComponentMatcher; import org.eclipse.yasson.internal.DeserializationContextImpl; import org.eclipse.yasson.internal.JsonbConfigProperties; import org.eclipse.yasson.internal.JsonbContext; @@ -149,38 +150,29 @@ private ModelDeserializer deserializerChainInternal(LinkedList } else if (userTypeMapping.containsKey(rawType)) { Class userTypeRaw = userTypeMapping.get(rawType); ModelDeserializer deserializer = deserializerChain(userTypeRaw); - models.put(cachedItem, deserializer); - return deserializer; - } - Optional adapterBinding = adapterBinding(type, (ComponentBoundCustomization) propertyCustomization); - if (adapterBinding.isPresent()) { - AdapterBinding adapter = adapterBinding.get(); - Class toType = ReflectionUtils.getRawType(adapter.getToType()); - ClassModel targetModel = jsonbContext.getMappingContext().getOrCreateClassModel(toType); - ModelDeserializer typeDeserializer = typeDeserializer(toType, - targetModel.getClassCustomization(), - JustReturn.instance()); - if (typeDeserializer == null) { - typeDeserializer = deserializerChain(adapter.getToType()); - } - ModelDeserializer targetAdapterModel = typeDeserializer; - AdapterDeserializer adapterDeserializer = new AdapterDeserializer(adapter, JustReturn.instance()); - ModelDeserializer adapterDeser = (parser, context) -> { - Object fromJson = targetAdapterModel.deserialize(parser, context); - return adapterDeserializer.deserialize(fromJson, context); - }; - models.put(cachedItem, adapterDeser); - return adapterDeser; + return cacheDeserializer(cachedItem, deserializer); } - ModelDeserializer typeDeserializer = typeDeserializer(rawType, - propertyCustomization, - JustReturn.instance()); - if (typeDeserializer != null) { - models.put(cachedItem, typeDeserializer); - return typeDeserializer; + + ClassCustomization classCustomization = classModel.getClassCustomization(); + ModelDeserializer deserializer = createUserOrAdapterDeserializer(type, (ComponentBoundCustomization) propertyCustomization, + (adapterToType) -> { + Class toType = ReflectionUtils.getRawType(adapterToType); + return typeDeserializer(toType, + jsonbContext.getMappingContext().getOrCreateClassModel(toType).getClassCustomization(), + JustReturn.instance()); + }, + JustReturn.instance(), + context -> context, + classCustomization, + () -> typeDeserializer(rawType, + propertyCustomization, + JustReturn.instance())); + if (deserializer != null) { + return cacheDeserializer(cachedItem, deserializer); } + if (Collection.class.isAssignableFrom(rawType)) { - return createCollectionDeserializer(cachedItem, rawType, chain, propertyCustomization); + return createCollectionDeserializer(cachedItem, rawType, chain); } else if (Map.class.isAssignableFrom(rawType)) { return createMapDeserializer(cachedItem, rawType, chain, propertyCustomization); } else if (rawType.isArray()) { @@ -190,53 +182,37 @@ private ModelDeserializer deserializerChainInternal(LinkedList } else if (Optional.class.isAssignableFrom(rawType)) { return createOptionalDeserializer(chain, type, propertyCustomization, cachedItem); } else { - return createObjectDeserializer(chain, type, propertyCustomization, classModel, rawType, cachedItem); + return createObjectDeserializer(chain, classModel, rawType, cachedItem); } } private ModelDeserializer createObjectDeserializer(LinkedList chain, - Type type, - Customization propertyCustomization, ClassModel classModel, Class rawType, CachedItem cachedItem) { ClassCustomization classCustomization = classModel.getClassCustomization(); - Optional> deserializerBinding = userDeserializer(type, - (ComponentBoundCustomization) propertyCustomization); - if (deserializerBinding.isPresent()) { - UserDefinedDeserializer user = new UserDefinedDeserializer(deserializerBinding.get().getJsonbDeserializer(), - JustReturn.instance(), type, classCustomization); - models.put(cachedItem, user); - return user; - } JsonbCreator creator = classCustomization.getCreator(); boolean hasCreator = creator != null; List params = hasCreator ? creatorParamsList(creator) : Collections.emptyList(); Function renamer = propertyRenamer(); - Map> processors = new LinkedHashMap<>(); - Map> defaultCreatorValues = new HashMap<>(); - for (PropertyModel propertyModel : classModel.getSortedProperties()) { - if (!propertyModel.isWritable() || params.contains(propertyModel.getReadName())) { - continue; - } - ModelDeserializer modelDeserializer = memberTypeProcessor(chain, propertyModel, hasCreator); - processors.put(renamer.apply(propertyModel.getReadName()), modelDeserializer); - } - for (String s : params) { - CreatorModel creatorModel = creator.findByName(s); - ModelDeserializer modelDeserializer = typeProcessor(chain, - creatorModel.getType(), - creatorModel.getCustomization(), - JustReturn.instance()); - String parameterName = renamer.apply(creatorModel.getName()); - processors.put(parameterName, modelDeserializer); - if (creatorModel.getCustomization().isRequired()) { - defaultCreatorValues.put(parameterName, new RequiredCreatorParameter(parameterName)); - } else { - Class rawParamType = ReflectionUtils.getRawType(creatorModel.getType()); - defaultCreatorValues.put(parameterName, DEFAULT_CREATOR_VALUES.getOrDefault(rawParamType, NULL_PROVIDER)); - } - } + Map> processors = Arrays.stream(classModel.getSortedProperties()) + .filter(propertyModel -> propertyModel.isWritable() && !params.contains(propertyModel.getReadName())) + .collect(Collectors.toMap(propertyModel -> renamer.apply(propertyModel.getReadName()), + propertyModel -> memberTypeProcessor(chain, propertyModel, hasCreator))); + processors.putAll(hasCreator ? params.stream() + .map(creator::findByName) + .collect(Collectors.toMap(creatorModel -> renamer.apply(creatorModel.getName()), + creatorModel -> typeProcessor(chain, + creatorModel.getType(), + creatorModel.getCustomization(), + JustReturn.instance()))) + : Collections.emptyMap()); + Map> defaultCreatorValues = hasCreator ? params.stream() + .map(creator::findByName) + .collect(Collectors.toMap(creatorModel -> renamer.apply(creatorModel.getName()), + creatorModel -> creatorModel.getCustomization().isRequired() ? new RequiredCreatorParameter(renamer.apply(creatorModel.getName())) + : DEFAULT_CREATOR_VALUES.getOrDefault(ReflectionUtils.getRawType(creatorModel.getType()), NULL_PROVIDER))) + : Collections.emptyMap(); ModelDeserializer instanceCreator; TypeInheritanceConfiguration typeInheritanceConfiguration = classCustomization.getPolymorphismConfig(); Set ignoredProperties = collectIgnoredProperties(typeInheritanceConfiguration); @@ -256,14 +232,12 @@ private ModelDeserializer createObjectDeserializer(LinkedList positionChecker = new PositionChecker(instanceCreator, rawType, Event.START_OBJECT); } ModelDeserializer nullChecker = new NullCheckDeserializer(positionChecker, JustReturn.instance()); - models.put(cachedItem, nullChecker); - return nullChecker; + return cacheDeserializer(cachedItem, nullChecker); } private ModelDeserializer createCollectionDeserializer(CachedItem cachedItem, Class rawType, - LinkedList chain, - Customization propertyCustomization) { + LinkedList chain) { Type type = cachedItem.type; Type colType = type instanceof ParameterizedType ? ((ParameterizedType) type).getActualTypeArguments()[0] @@ -278,8 +252,7 @@ private ModelDeserializer createCollectionDeserializer(CachedItem ca CollectionInstanceCreator instanceDeserializer = new CollectionInstanceCreator(collectionDeserializer, type); PositionChecker positionChecker = new PositionChecker(instanceDeserializer, rawType, Event.START_ARRAY); NullCheckDeserializer nullChecker = new NullCheckDeserializer(positionChecker, JustReturn.instance()); - models.put(cachedItem, nullChecker); - return nullChecker; + return cacheDeserializer(cachedItem, nullChecker); } private ModelDeserializer createMapDeserializer(CachedItem cachedItem, @@ -309,8 +282,7 @@ private ModelDeserializer createMapDeserializer(CachedItem cachedIte rawType); PositionChecker positionChecker = new PositionChecker(mapInstanceCreator, rawType, PositionChecker.Checker.CONTAINER); NullCheckDeserializer nullChecker = new NullCheckDeserializer(positionChecker, JustReturn.instance()); - models.put(cachedItem, nullChecker); - return nullChecker; + return cacheDeserializer(cachedItem, nullChecker); } private ModelDeserializer createArrayDeserializer(CachedItem cachedItem, @@ -327,8 +299,7 @@ private ModelDeserializer createArrayDeserializer(CachedItem cachedI ModelDeserializer base64Deserializer = ArrayInstanceCreator.createBase64Deserializer(strategy, typeProcessor); NullCheckDeserializer nullChecker = new NullCheckDeserializer(base64Deserializer, JustReturn.instance()); - models.put(cachedItem, nullChecker); - return nullChecker; + return cacheDeserializer(cachedItem, nullChecker); } Class arrayType = rawType.getComponentType(); ModelDeserializer typeProcessor = typeProcessor(chain, @@ -359,8 +330,7 @@ private ModelDeserializer createArrayCommonDeserializer(CachedItem c ArrayInstanceCreator arrayInstanceCreator = ArrayInstanceCreator.create(rawType, component, arrayDeserializer); PositionChecker positionChecker = new PositionChecker(arrayInstanceCreator, rawType, Event.START_ARRAY); NullCheckDeserializer nullChecker = new NullCheckDeserializer(positionChecker, JustReturn.instance()); - models.put(cachedItem, nullChecker); - return nullChecker; + return cacheDeserializer(cachedItem, nullChecker); } private OptionalDeserializer createOptionalDeserializer(LinkedList chain, @@ -372,8 +342,7 @@ private OptionalDeserializer createOptionalDeserializer(LinkedList chain, : Object.class; ModelDeserializer typeProcessor = typeProcessor(chain, colType, propertyCustomization, JustReturn.instance()); OptionalDeserializer optionalDeserializer = new OptionalDeserializer(typeProcessor, JustReturn.instance()); - models.put(cachedItem, optionalDeserializer); - return optionalDeserializer; + return cacheDeserializer(cachedItem, optionalDeserializer); } private Set collectIgnoredProperties(TypeInheritanceConfiguration typeInheritanceConfiguration) { @@ -389,22 +358,49 @@ private Set collectIgnoredProperties(TypeInheritanceConfiguration typeIn } private Function propertyRenamer() { - boolean isCaseInsensitive = jsonbContext.getConfig() + return jsonbContext.getConfig() .getProperty(PROPERTY_NAMING_STRATEGY) .filter(prop -> prop.equals(PropertyNamingStrategy.CASE_INSENSITIVE)) - .isPresent(); - - return isCaseInsensitive - ? String::toLowerCase - : value -> value; + .map(val -> (Function) String::toLowerCase).orElse(value -> value); } - private Optional adapterBinding(Type type, ComponentBoundCustomization classCustomization) { - return jsonbContext.getComponentMatcher().getDeserializeAdapterBinding(type, classCustomization); - } - - private Optional> userDeserializer(Type type, ComponentBoundCustomization classCustomization) { - return jsonbContext.getComponentMatcher().getDeserializerBinding(type, classCustomization); + /** + * Create a deserializer from deserializer's or adapter's description in the given type. + * + * @param type type to find the information about deserializer or adapter + * @param customization customization witch probably has information about deserializer or adapter + * @param adapterTypeDeserializerCreateFunction function to create a deserializer for the type in which adapter maps + * @param delegate delegate deserializer for the deserializer created from deserializer or adapter + * @param adapterTypeDeserializerContextChangeFunction function to change the context of the deserializer for the type in which adapter maps, if + * needed + * @param deserializerCustomization customization to use on deserializer creation from deserializer information + * @param alternativeDeserializerSupplier supplier of the deserializer if no adapter or deserializer configuration was found for given + * customization + * @return deserializer for the given type or alternative deserializer if no deserializer or adapter was configured for the class or property + */ + private ModelDeserializer createUserOrAdapterDeserializer(Type type, ComponentBoundCustomization customization, + Function> adapterTypeDeserializerCreateFunction, + ModelDeserializer delegate, + Function adapterTypeDeserializerContextChangeFunction, + Customization deserializerCustomization, + Supplier> alternativeDeserializerSupplier) { + // try to create a deserializer from deserializer configuration + ComponentMatcher componentMatcher = jsonbContext.getComponentMatcher(); + Optional> deserializerBinding = componentMatcher.getDeserializerBinding(type, customization); + return deserializerBinding.map(binding -> + (ModelDeserializer) new UserDefinedDeserializer(binding.getComponent(), delegate, type, deserializerCustomization)) + .orElseGet(() -> { + // otherwise try to create a deserializer from adapter configuration or use alternative deserializer + Optional> adapterBinding = componentMatcher.getDeserializeAdapterBinding(type, customization); + return adapterBinding.map(adapter -> { + AdapterDeserializer adapterDeserializer = new AdapterDeserializer(adapter, delegate); + Type toType = adapter.getToType(); + ModelDeserializer typeDeserializer = adapterTypeDeserializerCreateFunction.apply(toType); + ModelDeserializer targetAdapterModel = typeDeserializer == null ? deserializerChain(toType) : typeDeserializer; + return (ModelDeserializer) (parser, context) -> + adapterDeserializer.deserialize(targetAdapterModel.deserialize(parser, adapterTypeDeserializerContextChangeFunction.apply(context)), context); + }).orElseGet(alternativeDeserializerSupplier); + }); } private List creatorParamsList(JsonbCreator creator) { @@ -437,46 +433,24 @@ private ModelDeserializer typeProcessor(LinkedList chain, Set events) { Type resolved = ReflectionUtils.resolveType(chain, type); Class rawType = ReflectionUtils.getRawType(resolved); - Optional> deserializerBinding = userDeserializer(resolved, - (ComponentBoundCustomization) customization); - if (deserializerBinding.isPresent()) { - //TODO remove or not? fix for deserializer cycle - // ModelDeserializer exactType = createNewChain(chain, memberDeserializer, rawType, - // resolved, customization); - // return new UserDefinedDeserializer(deserializerBinding.get().getJsonbDeserializer(), - // exactType, - // memberDeserializer, - // resolved, - // customization); - return new UserDefinedDeserializer(deserializerBinding.get().getJsonbDeserializer(), - memberDeserializer, - resolved, - customization); - } - Optional adapterBinding = adapterBinding(resolved, (ComponentBoundCustomization) customization); - if (adapterBinding.isPresent()) { - AdapterBinding adapter = adapterBinding.get(); - ModelDeserializer typeDeserializer = typeDeserializer(ReflectionUtils.getRawType(adapter.getToType()), - customization, - JustReturn.instance(), events); - if (typeDeserializer == null) { - typeDeserializer = deserializerChain(adapter.getToType()); - } - ModelDeserializer targetAdapterModel = typeDeserializer; - - AdapterDeserializer adapterDeserializer = new AdapterDeserializer(adapter, memberDeserializer); - return (parser, context) -> { - DeserializationContextImpl newContext = new DeserializationContextImpl(context); - Object fromJson = targetAdapterModel.deserialize(parser, newContext); - return adapterDeserializer.deserialize(fromJson, context); - }; - } - ModelDeserializer typeDeserializer = typeDeserializer(rawType, customization, memberDeserializer, events); - if (typeDeserializer == null) { - Class implClass = resolveImplClass(rawType, customization); - return createNewChain(chain, memberDeserializer, implClass, resolved, customization); + /* TODO remove or not? fix for deserializer cycle + ModelDeserializer exactType = createNewChain(chain, memberDeserializer, rawType, + resolved, customization); + return new UserDefinedDeserializer(deserializerBinding.get().getJsonbDeserializer(), + exactType, + memberDeserializer, + resolved, + customization);*/ + ModelDeserializer deserializer = createUserOrAdapterDeserializer(resolved, (ComponentBoundCustomization) customization, + (adapterToType) -> typeDeserializer(ReflectionUtils.getRawType(adapterToType), customization, JustReturn.instance(), events), + memberDeserializer, + DeserializationContextImpl::new, + customization, + () -> typeDeserializer(rawType, customization, memberDeserializer, events)); + if (deserializer != null) { + return deserializer; } - return typeDeserializer; + return createNewChain(chain, memberDeserializer, resolveImplClass(rawType, customization), resolved, customization); } private ModelDeserializer createNewChain(LinkedList chain, @@ -500,7 +474,7 @@ private ModelDeserializer typeDeserializer(Class rawType, ModelDeserializer delegate, Set events) { return TypeDeserializers - .getTypeDeserializer(rawType, customization, jsonbContext.getConfigProperties(), delegate, events); + .getTypeDeserializer(rawType, customization, jsonbContext, delegate, events); } private Class resolveImplClass(Class rawType, Customization customization) { @@ -530,6 +504,11 @@ private CachedItem createCachedItem(Type type, Customization customization) { return new CachedItem(type, customization.getDeserializeNumberFormatter(), customization.getDeserializeDateFormatter()); } + private > T cacheDeserializer(CachedItem cachedItem, T deserializer) { + models.put(cachedItem, deserializer); + return deserializer; + } + private static final class CachedItem { private final Type type; diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/InheritanceInstanceCreator.java b/src/main/java/org/eclipse/yasson/internal/deserializer/InheritanceInstanceCreator.java index efcc6c54e..9611c24a2 100644 --- a/src/main/java/org/eclipse/yasson/internal/deserializer/InheritanceInstanceCreator.java +++ b/src/main/java/org/eclipse/yasson/internal/deserializer/InheritanceInstanceCreator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024 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 @@ -48,15 +48,13 @@ class InheritanceInstanceCreator implements ModelDeserializer { @Override public Object deserialize(JsonParser parser, DeserializationContextImpl context) { - String alias; - JsonParser jsonParser; String polymorphismKeyName = typeInheritanceConfiguration.getFieldName(); JsonObject object = parser.getObject(); - alias = object.getString(polymorphismKeyName, null); + String alias = object.getString(polymorphismKeyName, null); JsonObject newJsonObject = context.getJsonbContext().getJsonProvider().createObjectBuilder(object) .remove(polymorphismKeyName) .build(); - jsonParser = new JsonStructureToParserAdapter(newJsonObject); + JsonParser jsonParser = new JsonStructureToParserAdapter(newJsonObject, context.getJsonbContext().getJsonProvider()); //To get to the first event Event event = jsonParser.next(); context.setLastValueEvent(event); diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/JsonbCreatorDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/JsonbCreatorDeserializer.java index e3dab9a4e..bd1724796 100644 --- a/src/main/java/org/eclipse/yasson/internal/deserializer/JsonbCreatorDeserializer.java +++ b/src/main/java/org/eclipse/yasson/internal/deserializer/JsonbCreatorDeserializer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024 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 @@ -98,15 +98,9 @@ public Object deserialize(JsonParser parser, DeserializationContextImpl context) } break; case END_OBJECT: - Object[] params = new Object[creatorParams.size()]; - for (int i = 0; i < creatorParams.size(); i++) { - String param = creatorParams.get(i); - if (paramValues.containsKey(param)) { - params[i] = paramValues.get(param); - } else { - params[i] = defaultCreatorValues.get(param).deserialize(null, context); - } - } + Object[] params = creatorParams.stream() + .map(param -> paramValues.containsKey(param) ? paramValues.get(param) : defaultCreatorValues.get(param).deserialize(null, context)) + .toArray(); context.setInstance(creator.call(params, clazz)); context.getDeferredDeserializers().forEach(Runnable::run); context.getDeferredDeserializers().clear(); diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/MapDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/MapDeserializer.java index d366fc8fb..19043ad94 100644 --- a/src/main/java/org/eclipse/yasson/internal/deserializer/MapDeserializer.java +++ b/src/main/java/org/eclipse/yasson/internal/deserializer/MapDeserializer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024 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 @@ -33,7 +33,7 @@ class MapDeserializer implements ModelDeserializer { this.valueDelegate = valueDelegate; } - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "fallthrough"}) @Override public Object deserialize(JsonParser parser, DeserializationContextImpl context) { Map map = (Map) context.getInstance(); diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/UserDefinedDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/UserDefinedDeserializer.java index 70ec44014..649c1b259 100644 --- a/src/main/java/org/eclipse/yasson/internal/deserializer/UserDefinedDeserializer.java +++ b/src/main/java/org/eclipse/yasson/internal/deserializer/UserDefinedDeserializer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024 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 @@ -30,18 +30,18 @@ class UserDefinedDeserializer implements ModelDeserializer { private final Type rType; private final Customization customization; - //TODO remove or not? deserializer cycle - // public UserDefinedDeserializer(JsonbDeserializer userDefinedDeserializer, - // ModelDeserializer exactType, - // ModelDeserializer delegate, - // Type rType, - // Customization customization) { - // this.userDefinedDeserializer = userDefinedDeserializer; - // this.exactType = exactType; - // this.delegate = delegate; - // this.rType = rType; - // this.customization = customization; - // } + /*TODO remove or not? deserializer cycle + public UserDefinedDeserializer(JsonbDeserializer userDefinedDeserializer, + ModelDeserializer exactType, + ModelDeserializer delegate, + Type rType, + Customization customization) { + this.userDefinedDeserializer = userDefinedDeserializer; + this.exactType = exactType; + this.delegate = delegate; + this.rType = rType; + this.customization = customization; + }*/ UserDefinedDeserializer(JsonbDeserializer userDefinedDeserializer, ModelDeserializer delegate, Type rType, @@ -56,15 +56,15 @@ class UserDefinedDeserializer implements ModelDeserializer { public Object deserialize(JsonParser value, DeserializationContextImpl context) { DeserializationContextImpl newContext = new DeserializationContextImpl(context); newContext.setCustomization(customization); - //TODO remove or not? deserializer cycle - // if (context.getUserProcessorChain().contains(userDefinedDeserializer.getClass())) { - // if (context.getLastValueEvent() != JsonParser.Event.START_ARRAY - // && context.getLastValueEvent() != JsonParser.Event.START_OBJECT) { - // newContext.setDisableNextPositionCheck(true); - // } - // return exactType.deserialize(value, newContext); - // } - // newContext.getUserProcessorChain().add(userDefinedDeserializer.getClass()); + /*TODO remove or not? deserializer cycle + if (context.getUserProcessorChain().contains(userDefinedDeserializer.getClass())) { + if (context.getLastValueEvent() != JsonParser.Event.START_ARRAY + && context.getLastValueEvent() != JsonParser.Event.START_OBJECT) { + newContext.setDisableNextPositionCheck(true); + } + return exactType.deserialize(value, newContext); + } + newContext.getUserProcessorChain().add(userDefinedDeserializer.getClass());*/ YassonParser yassonParser = new YassonParser(value, context.getLastValueEvent(), newContext); Object object = userDefinedDeserializer.deserialize(yassonParser, newContext, rType); yassonParser.skipRemaining(); diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/YassonParser.java b/src/main/java/org/eclipse/yasson/internal/deserializer/YassonParser.java index c17f6ff56..bae43bba2 100644 --- a/src/main/java/org/eclipse/yasson/internal/deserializer/YassonParser.java +++ b/src/main/java/org/eclipse/yasson/internal/deserializer/YassonParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024 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 @@ -13,6 +13,8 @@ package org.eclipse.yasson.internal.deserializer; import java.math.BigDecimal; +import java.util.ArrayDeque; +import java.util.Deque; import java.util.Map; import java.util.NoSuchElementException; import java.util.stream.Stream; @@ -24,6 +26,9 @@ import jakarta.json.stream.JsonParser; import org.eclipse.yasson.internal.DeserializationContextImpl; +import org.eclipse.yasson.internal.JsonParserStreamCreator; +import org.eclipse.yasson.internal.properties.MessageKeys; +import org.eclipse.yasson.internal.properties.Messages; /** * Yasson {@link YassonParser} parser wrapper. @@ -34,21 +39,28 @@ class YassonParser implements JsonParser { private final JsonParser delegate; private final DeserializationContextImpl context; - private int level; + private final JsonParserStreamCreator streamCreator; + private final Deque contextStack = new ArrayDeque<>(); YassonParser(JsonParser delegate, Event firstEvent, DeserializationContextImpl context) { this.delegate = delegate; this.context = context; - this.level = determineLevelValue(firstEvent); + CurrentContext currentContext = determineLevelValue(firstEvent); + if (currentContext != null) { + contextStack.push(currentContext); + } + streamCreator = new JsonParserStreamCreator(this, false, context::getLastValueEvent, + () -> contextStack.size() == 1 && context.getLastValueEvent() == Event.START_OBJECT); } - private int determineLevelValue(Event firstEvent) { + private CurrentContext determineLevelValue(Event firstEvent) { switch (firstEvent) { case START_ARRAY: + return CurrentContext.ARRAY; //container start, there will be more events to come case START_OBJECT: - return 1; //container start, there will be more events to come + return CurrentContext.OBJECT; //container start, there will be more events to come default: - return 0; //just this single value, do not allow reading more + return null; //just this single value, do not allow reading more } } @@ -60,7 +72,7 @@ void skipRemaining() { @Override public boolean hasNext() { - if (level < 1) { + if (contextStack.isEmpty()) { return false; } return delegate.hasNext(); @@ -74,11 +86,14 @@ public Event next() { switch (next) { case START_OBJECT: case START_ARRAY: - level++; + CurrentContext currentContext = determineLevelValue(next); + if (currentContext != null) { + contextStack.push(currentContext); + } break; case END_OBJECT: case END_ARRAY: - level--; + contextStack.pop(); break; default: //no other changes needed @@ -86,6 +101,11 @@ public Event next() { return next; } + @Override + public Event currentEvent() { + return delegate.currentEvent(); + } + @Override public String getString() { return delegate.getString(); @@ -118,9 +138,12 @@ public JsonLocation getLocation() { @Override public JsonObject getObject() { + if (delegate.currentEvent() != Event.START_OBJECT) { + throw new IllegalStateException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "getObject() Not at the beginning of an object")); + } validate(); - level--; JsonObject jsonObject = delegate.getObject(); + contextStack.pop(); context.setLastValueEvent(Event.END_OBJECT); return jsonObject; } @@ -140,9 +163,12 @@ public JsonValue getValue() { @Override public JsonArray getArray() { + if (delegate.currentEvent() != Event.START_ARRAY) { + throw new IllegalStateException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "getArray() Not at the beginning of an array")); + } validate(); - level--; JsonArray array = delegate.getArray(); + contextStack.pop(); context.setLastValueEvent(Event.END_ARRAY); return array; } @@ -150,46 +176,52 @@ public JsonArray getArray() { @Override public Stream getArrayStream() { validate(); - level--; - return delegate.getArrayStream(); + return streamCreator.getArrayStream(); } @Override public Stream> getObjectStream() { validate(); - level--; - return delegate.getObjectStream(); + return streamCreator.getObjectStream(); } @Override public Stream getValueStream() { validate(); - level--; - return delegate.getValueStream(); + return streamCreator.getValueStream(); } @Override public void skipArray() { - validate(); - level--; - delegate.skipArray(); + if (contextStack.peek() == CurrentContext.ARRAY) { + delegate.skipArray(); + contextStack.pop(); + context.setLastValueEvent(Event.END_ARRAY); + } } @Override public void skipObject() { - validate(); - level--; - delegate.skipObject(); + if (contextStack.peek() == CurrentContext.OBJECT) { + delegate.skipObject(); + contextStack.pop(); + context.setLastValueEvent(Event.END_OBJECT); + } } @Override public void close() { - throw new UnsupportedOperationException(); + delegate.close(); } private void validate() { - if (level < 1) { + if (contextStack.isEmpty()) { throw new NoSuchElementException("There are no more elements available!"); } } + + private enum CurrentContext { + OBJECT, + ARRAY + } } diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/AbstractNumberDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/AbstractNumberDeserializer.java index e50e003a1..249813b15 100644 --- a/src/main/java/org/eclipse/yasson/internal/deserializer/types/AbstractNumberDeserializer.java +++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/AbstractNumberDeserializer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024 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 @@ -25,7 +25,6 @@ import org.eclipse.yasson.internal.DeserializationContextImpl; import org.eclipse.yasson.internal.JsonbNumberFormatter; import org.eclipse.yasson.internal.deserializer.ModelDeserializer; -import org.eclipse.yasson.internal.model.customization.Customization; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; @@ -44,8 +43,8 @@ abstract class AbstractNumberDeserializer extends TypeDeserial } private ModelDeserializer actualDeserializer(TypeDeserializerBuilder builder) { - Customization customization = builder.getCustomization(); - if (customization.getDeserializeNumberFormatter() == null) { + final JsonbNumberFormatter numberFormat = builder.getCustomization().getDeserializeNumberFormatter(); + if (numberFormat == null) { return (value, context) -> { try { return parseNumberValue(value); @@ -55,7 +54,6 @@ private ModelDeserializer actualDeserializer(TypeDeserializerBuilder bui }; } - final JsonbNumberFormatter numberFormat = customization.getDeserializeNumberFormatter(); //consider synchronizing on format instance or per thread cache. Locale locale = builder.getConfigProperties().getLocale(numberFormat.getLocale()); final NumberFormat format = NumberFormat.getInstance(locale); diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/EnumDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/EnumDeserializer.java index f32aeb86e..2a304b633 100644 --- a/src/main/java/org/eclipse/yasson/internal/deserializer/types/EnumDeserializer.java +++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/EnumDeserializer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024 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 @@ -13,21 +13,51 @@ package org.eclipse.yasson.internal.deserializer.types; import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Map; import org.eclipse.yasson.internal.DeserializationContextImpl; +import org.eclipse.yasson.internal.model.ClassModel; +import org.eclipse.yasson.internal.model.PropertyModel; /** * Deserializer of the {@link Enum}. */ class EnumDeserializer extends TypeDeserializer { + private final Map> nameToConstantMap; + EnumDeserializer(TypeDeserializerBuilder builder) { super(builder); + + nameToConstantMap = createNameToConstantMap(builder); + } + + private static > Map createNameToConstantMap(TypeDeserializerBuilder builder) { + Map nameToConstantMap = null; + Class clazz = builder.getClazz(); + + if (clazz.isEnum()) { + try { + @SuppressWarnings("unchecked") + Class enumClazz = (Class) clazz; + nameToConstantMap = new HashMap<>(); + ClassModel classModel = builder.getJsonbContext().getMappingContext().getOrCreateClassModel(clazz); + + for (E enumConstant : enumClazz.getEnumConstants()) { + PropertyModel model = classModel.getPropertyModel(enumConstant.name()); + nameToConstantMap.put(model.getReadName(), enumConstant); + } + } catch (ClassCastException classCastException) { + throw new IllegalArgumentException("EnumDeserializer can only be used with Enum types"); + } + } + return nameToConstantMap; } - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "rawtypes"}) @Override Object deserializeStringValue(String value, DeserializationContextImpl context, Type rType) { - return Enum.valueOf((Class) rType, value); + return nameToConstantMap == null ? Enum.valueOf((Class) rType, value) : nameToConstantMap.get(value); } } diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/TypeDeserializerBuilder.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/TypeDeserializerBuilder.java index c15ee2fc1..3ca68fcab 100644 --- a/src/main/java/org/eclipse/yasson/internal/deserializer/types/TypeDeserializerBuilder.java +++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/TypeDeserializerBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024 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 @@ -15,6 +15,7 @@ import java.util.Objects; import org.eclipse.yasson.internal.JsonbConfigProperties; +import org.eclipse.yasson.internal.JsonbContext; import org.eclipse.yasson.internal.deserializer.ModelDeserializer; import org.eclipse.yasson.internal.model.customization.ClassCustomization; import org.eclipse.yasson.internal.model.customization.Customization; @@ -23,16 +24,16 @@ class TypeDeserializerBuilder { private final Class clazz; private final Customization customization; - private final JsonbConfigProperties configProperties; + private final JsonbContext jsonbContext; private final ModelDeserializer delegate; TypeDeserializerBuilder(Class clazz, Customization customization, - JsonbConfigProperties configProperties, + JsonbContext jsonbContext, ModelDeserializer delegate) { this.clazz = Objects.requireNonNull(clazz); this.customization = customization == null ? ClassCustomization.empty() : customization; - this.configProperties = configProperties; + this.jsonbContext = jsonbContext; this.delegate = Objects.requireNonNull(delegate); } @@ -41,7 +42,11 @@ public Class getClazz() { } public JsonbConfigProperties getConfigProperties() { - return configProperties; + return jsonbContext.getConfigProperties(); + } + + public JsonbContext getJsonbContext() { + return jsonbContext; } public ModelDeserializer getDelegate() { diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/TypeDeserializers.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/TypeDeserializers.java index de1f7e00c..8c5e88489 100644 --- a/src/main/java/org/eclipse/yasson/internal/deserializer/types/TypeDeserializers.java +++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/TypeDeserializers.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024 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 @@ -51,7 +51,7 @@ import jakarta.json.bind.JsonbException; import jakarta.json.stream.JsonParser; -import org.eclipse.yasson.internal.JsonbConfigProperties; +import org.eclipse.yasson.internal.JsonbContext; import org.eclipse.yasson.internal.deserializer.JustReturn; import org.eclipse.yasson.internal.deserializer.ModelDeserializer; import org.eclipse.yasson.internal.deserializer.NullCheckDeserializer; @@ -135,14 +135,14 @@ private TypeDeserializers() { * * @param clazz type to create deserializer for * @param customization type customization - * @param properties config properties + * @param jsonbContext config properties * @param delegate delegate to be called by the created deserializer * @param events expected parser events at the beginning when deserializing the type * @return type deserializer */ public static ModelDeserializer getTypeDeserializer(Class clazz, Customization customization, - JsonbConfigProperties properties, + JsonbContext jsonbContext, ModelDeserializer delegate, Set events) { JsonParser.Event[] eventArray = events.toArray(new JsonParser.Event[0]); @@ -150,7 +150,7 @@ public static ModelDeserializer getTypeDeserializer(Class clazz, Class optionalType = OPTIONAL_TYPES.get(clazz); TypeDeserializerBuilder builder = new TypeDeserializerBuilder(optionalType, customization, - properties, + jsonbContext, JustReturn.instance()); ValueExtractor valueExtractor = new ValueExtractor(DESERIALIZERS.get(optionalType).apply(builder)); PositionChecker positionChecker = new PositionChecker(valueExtractor, clazz, eventArray); @@ -165,7 +165,7 @@ public static ModelDeserializer getTypeDeserializer(Class clazz, } } - TypeDeserializerBuilder builder = new TypeDeserializerBuilder(clazz, customization, properties, delegate); + TypeDeserializerBuilder builder = new TypeDeserializerBuilder(clazz, customization, jsonbContext, delegate); if (DESERIALIZERS.containsKey(clazz)) { ValueExtractor valueExtractor = new ValueExtractor(DESERIALIZERS.get(clazz).apply(builder)); return new NullCheckDeserializer(new PositionChecker(valueExtractor, clazz, eventArray), delegate); diff --git a/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonObjectIterator.java b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonObjectIterator.java index 4ba94c7dc..ac6452266 100644 --- a/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonObjectIterator.java +++ b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonObjectIterator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024 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 @@ -75,20 +75,20 @@ public JsonParser.Event next() { case START: if (keyIterator.hasNext()) { nextKey(); - setState(JsonObjectIterator.State.KEY); + setState(State.KEY); return JsonParser.Event.KEY_NAME; } else { setState(State.END); return JsonParser.Event.END_OBJECT; } case KEY: - setState(JsonObjectIterator.State.VALUE); + setState(State.VALUE); JsonValue value = getValue(); return getValueEvent(value); case VALUE: if (keyIterator.hasNext()) { nextKey(); - setState(JsonObjectIterator.State.KEY); + setState(State.KEY); return JsonParser.Event.KEY_NAME; } setState(State.END); @@ -119,7 +119,7 @@ public JsonValue getValue() { @Override String getString() { - if (state == JsonObjectIterator.State.KEY) { + if (state == State.KEY) { return currentKey; } return super.getString(); diff --git a/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonStructureIterator.java b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonStructureIterator.java index 1e402b57d..b0819451b 100644 --- a/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonStructureIterator.java +++ b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonStructureIterator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024 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 @@ -67,9 +67,11 @@ JsonParser.Event getValueEvent(JsonValue value) { case NUMBER: return JsonParser.Event.VALUE_NUMBER; case STRING: + return JsonParser.Event.VALUE_STRING; case TRUE: + return JsonParser.Event.VALUE_TRUE; case FALSE: - return JsonParser.Event.VALUE_STRING; + return JsonParser.Event.VALUE_FALSE; case OBJECT: return JsonParser.Event.START_OBJECT; case ARRAY: diff --git a/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonStructureToParserAdapter.java b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonStructureToParserAdapter.java index dfa9d64f4..9f326ed55 100644 --- a/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonStructureToParserAdapter.java +++ b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonStructureToParserAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024 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 @@ -15,6 +15,11 @@ import java.math.BigDecimal; import java.util.ArrayDeque; import java.util.Deque; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.function.Predicate; +import java.util.stream.Stream; import jakarta.json.JsonArray; import jakarta.json.JsonNumber; @@ -22,65 +27,101 @@ import jakarta.json.JsonStructure; import jakarta.json.JsonValue; import jakarta.json.bind.JsonbException; +import jakarta.json.spi.JsonProvider; import jakarta.json.stream.JsonLocation; import jakarta.json.stream.JsonParser; +import org.eclipse.yasson.internal.JsonParserStreamCreator; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; /** * Adapter for {@link JsonParser}, that reads a {@link JsonStructure} content tree instead of JSON text. - * + *

* Yasson and jsonb API components are using {@link JsonParser} as its input API. * This adapter allows deserialization of {@link JsonStructure} into java content tree using same components * as when parsing JSON text. */ public class JsonStructureToParserAdapter implements JsonParser { - private Deque iterators = new ArrayDeque<>(); + private final Deque iterators = new ArrayDeque<>(); private final JsonStructure rootStructure; + private final JsonProvider jsonProvider; + + //JsonParserImpl delivers the whole object - so we have to call next() before creation of the stream + private final JsonParserStreamCreator streamCreator = new JsonParserStreamCreator(this, true, this::currentEvent, iterators::isEmpty); + + private Event currentEvent; /** * Creates new {@link JsonStructure} parser. * - * @param structure json structure + * @param structure json structure + * @param jsonProvider json provider for creation of {@link JsonValue} for keys */ - public JsonStructureToParserAdapter(JsonStructure structure) { + public JsonStructureToParserAdapter(JsonStructure structure, JsonProvider jsonProvider) { this.rootStructure = structure; + this.jsonProvider = jsonProvider; } @Override public boolean hasNext() { - return iterators.peek().hasNext(); + JsonStructureIterator iterator = iterators.peek(); + return (iterator != null) && iterator.hasNext(); } @Override public Event next() { if (iterators.isEmpty()) { - if (rootStructure instanceof JsonObject) { - iterators.push(new JsonObjectIterator((JsonObject) rootStructure)); - return Event.START_OBJECT; - } else if (rootStructure instanceof JsonArray) { - iterators.push(new JsonArrayIterator((JsonArray) rootStructure)); - return Event.START_ARRAY; - } + currentEvent = pushIntoIterators(rootStructure, rootStructure instanceof JsonObject, Event.START_OBJECT, + rootStructure instanceof JsonArray, Event.START_ARRAY); + return currentEvent; } JsonStructureIterator current = iterators.peek(); - Event next = current.next(); - if (next == Event.START_OBJECT) { - iterators.push(new JsonObjectIterator((JsonObject) current.getValue())); - } else if (next == Event.START_ARRAY) { - iterators.push(new JsonArrayIterator((JsonArray) current.getValue())); - } else if (next == Event.END_OBJECT || next == Event.END_ARRAY) { + currentEvent = current.next(); + pushIntoIterators(current.getValue(), currentEvent == Event.START_OBJECT, null, currentEvent == Event.START_ARRAY, null); + if (currentEvent == Event.END_OBJECT || currentEvent == Event.END_ARRAY) { iterators.pop(); } - return next; + return currentEvent; + } + + private Event pushIntoIterators(JsonValue value, boolean isObject, Event objectEvent, boolean isArray, Event arrayEvent) { + if (isObject) { + iterators.push(new JsonObjectIterator((JsonObject) value)); + return objectEvent; + } else if (isArray) { + iterators.push(new JsonArrayIterator((JsonArray) value)); + return arrayEvent; + } + return null; + } + + @Override + public Event currentEvent() { + return currentEvent; } @Override public String getString() { - return iterators.peek().getString(); + if (currentEvent == null) { + throw new IllegalStateException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "getString() call with current event: null")); + } + + switch (currentEvent) { + case KEY_NAME: + case VALUE_STRING: + case VALUE_NUMBER: + JsonStructureIterator iterator = iterators.peek(); + if (iterator == null) { + throw new IllegalStateException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "getString() call with empty internal stack")); + } + return iterator.getString(); + default: + throw new IllegalStateException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "getString() call with current event: " + + currentEvent + "; should be in [KEY_NAME, VALUE_STRING, VALUE_NUMBER]")); + } } @Override @@ -106,17 +147,22 @@ public BigDecimal getBigDecimal() { @Override public JsonObject getObject() { JsonStructureIterator current = iterators.peek(); - if (current instanceof JsonObjectIterator) { + if (currentEvent == Event.START_OBJECT) { //Remove child iterator as getObject() method contract says iterators.pop(); - return current.getValue().asJsonObject(); + currentEvent = Event.END_OBJECT; + JsonValue value = current == null ? null : current.getValue(); + return value == null ? null : value.asJsonObject(); } else { - throw new JsonbException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "Outside of object context")); + throw new IllegalStateException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "getObject() Not at the beginning of an object")); } } private JsonNumber getJsonNumberValue() { JsonStructureIterator iterator = iterators.peek(); + if (iterator == null) { + throw new IllegalStateException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "Call of the number method on empty context")); + } JsonValue value = iterator.getValue(); if (value.getValueType() != JsonValue.ValueType.NUMBER) { throw iterator.createIncompatibleValueError(); @@ -130,21 +176,80 @@ public JsonLocation getLocation() { } @Override - public void skipArray() { - if (!iterators.isEmpty()) { + public JsonValue getValue() { + if (currentEvent == null) { + throw new IllegalStateException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "getValue() call with current event: null")); + } else { + JsonStructureIterator iterator = iterators.peek(); + if (iterator == null) { + throw new IllegalStateException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "getValue() call empty internal stack")); + } else { + switch (currentEvent) { + case START_OBJECT: + return getObject(); + case START_ARRAY: + return getArray(); + case KEY_NAME: + return jsonProvider.createValue(iterator.getString()); + case END_ARRAY: + case END_OBJECT: + throw new IllegalStateException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "getValue() call with current event: " + + currentEvent + "; should not be in [END_OBJECT, END_ARRAY]")); + default: + return iterator.getValue(); + } + } + } + } + + @Override + public JsonArray getArray() { + if (currentEvent == Event.START_ARRAY) { + //Remove child iterator as getArray() method contract says + iterators.pop(); + currentEvent = Event.END_ARRAY; JsonStructureIterator current = iterators.peek(); - if (current instanceof JsonArrayIterator) { - iterators.pop(); + if (current == null) { + throw new NoSuchElementException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "No more elements in JSON structure")); } + return current.getValue().asJsonArray(); + } else { + throw new IllegalStateException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "getArray() not at the beginning of an array")); } } + @Override + public Stream getArrayStream() { + return streamCreator.getArrayStream(); + } + + @Override + public Stream> getObjectStream() { + return streamCreator.getObjectStream(); + } + + @Override + public Stream getValueStream() { + return streamCreator.getValueStream(); + } + + @Override + public void skipArray() { + skipJsonPart(iterator -> iterator instanceof JsonArrayIterator, Event.END_ARRAY); + } + @Override public void skipObject() { + skipJsonPart(iterator -> iterator instanceof JsonObjectIterator, Event.END_OBJECT); + } + + private void skipJsonPart(Predicate predicate, Event newCurrentEvent) { + Objects.requireNonNull(predicate); if (!iterators.isEmpty()) { JsonStructureIterator current = iterators.peek(); - if (current instanceof JsonObjectIterator) { + if (predicate.test(current)) { iterators.pop(); + currentEvent = newCurrentEvent; } } } @@ -153,4 +258,4 @@ public void skipObject() { public void close() { //noop } -} +} \ No newline at end of file diff --git a/src/main/java/org/eclipse/yasson/internal/model/CreatorModel.java b/src/main/java/org/eclipse/yasson/internal/model/CreatorModel.java index 3ab3d9054..becdfa23b 100644 --- a/src/main/java/org/eclipse/yasson/internal/model/CreatorModel.java +++ b/src/main/java/org/eclipse/yasson/internal/model/CreatorModel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024 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 @@ -12,7 +12,6 @@ package org.eclipse.yasson.internal.model; -import java.lang.reflect.Executable; import java.lang.reflect.Parameter; import java.lang.reflect.Type; @@ -39,22 +38,22 @@ public class CreatorModel { * Creates a new instance. * @param name Parameter name * @param parameter constructor parameter - * @param executable creator executable +// * @param executable creator executable * @param context jsonb context */ - public CreatorModel(String name, Parameter parameter, Executable executable, JsonbContext context) { + public CreatorModel(String name, Parameter parameter, /*Executable executable,*/ JsonbContext context) { this.name = name; this.type = parameter.getParameterizedType(); AnnotationIntrospector annotationIntrospector = context.getAnnotationIntrospector(); JsonbAnnotatedElement annotated = new JsonbAnnotatedElement<>(parameter); - boolean required = context.getAnnotationIntrospector().requiredParameters(executable, annotated); + boolean required = context.getAnnotationIntrospector().requiredParameters(/*executable, annotated*/); JsonbNumberFormatter constructorNumberFormatter = context.getAnnotationIntrospector() .getConstructorNumberFormatter(annotated); JsonbDateFormatter constructorDateFormatter = context.getAnnotationIntrospector().getConstructorDateFormatter(annotated); DeserializerBinding deserializerBinding = annotationIntrospector.getDeserializerBinding(parameter); - AdapterBinding adapterBinding = annotationIntrospector.getAdapterBinding(parameter); + AdapterBinding adapterBinding = annotationIntrospector.getAdapterBinding(parameter); final JsonbAnnotatedElement> clsElement = annotationIntrospector.collectAnnotations(parameter.getType()); deserializerBinding = deserializerBinding == null ? annotationIntrospector.getDeserializerBinding(clsElement) diff --git a/src/main/java/org/eclipse/yasson/internal/model/PropertyModel.java b/src/main/java/org/eclipse/yasson/internal/model/PropertyModel.java index 707b03a68..4b8a4c04c 100644 --- a/src/main/java/org/eclipse/yasson/internal/model/PropertyModel.java +++ b/src/main/java/org/eclipse/yasson/internal/model/PropertyModel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024 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 @@ -21,6 +21,7 @@ import java.lang.reflect.Type; import java.security.AccessController; import java.security.PrivilegedAction; +import java.util.Arrays; import java.util.EnumSet; import java.util.Map; import java.util.Objects; @@ -209,9 +210,9 @@ private PropertyCustomization introspectCustomization(Property property, JsonbCo } } } - if (transientInfo.size() != 0) { - builder.readTransient(transientInfo.contains(AnnotationTarget.GETTER)); - builder.writeTransient(transientInfo.contains(AnnotationTarget.SETTER)); + if (!transientInfo.isEmpty()) { + builder.readTransient(transientInfo.contains(AnnotationTarget.GETTER)) + .writeTransient(transientInfo.contains(AnnotationTarget.SETTER)); if (transientInfo.contains(AnnotationTarget.PROPERTY)) { if (!transientInfo.contains(AnnotationTarget.GETTER)) { @@ -233,39 +234,38 @@ private PropertyCustomization introspectCustomization(Property property, JsonbCo } if (!builder.readTransient()) { - builder.jsonWriteName(introspector.getJsonbPropertyJsonWriteName(property)); - builder.nillable(introspector.isPropertyNillable(property).orElse(classModel.getClassCustomization().isNillable())); - builder.serializerBinding(getUserSerializerBinding(property, jsonbContext)); + builder.jsonWriteName(introspector.getJsonbPropertyJsonWriteName(property)) + .nillable(introspector.isPropertyNillable(property).orElse(classModel.getClassCustomization().isNillable())) + .serializerBinding(getUserSerializerBinding(property, jsonbContext)); } if (!builder.writeTransient()) { - builder.jsonReadName(introspector.getJsonbPropertyJsonReadName(property)); - builder.deserializerBinding(introspector.getDeserializerBinding(property)); + builder.jsonReadName(introspector.getJsonbPropertyJsonReadName(property)) + .deserializerBinding(introspector.getDeserializerBinding(property)); } - final AdapterBinding adapterBinding = jsonbContext.getAnnotationIntrospector().getAdapterBinding(property); + final AdapterBinding adapterBinding = jsonbContext.getAnnotationIntrospector().getAdapterBinding(property); if (adapterBinding != null) { - builder.serializeAdapter(adapterBinding); - builder.deserializeAdapter(adapterBinding); + builder.deserializeAdapter(adapterBinding) + .serializeAdapter(adapterBinding); } else { builder.serializeAdapter(jsonbContext.getComponentMatcher() - .getSerializeAdapterBinding(getPropertySerializationType(), null).orElse(null)); - builder.deserializeAdapter(jsonbContext.getComponentMatcher() + .getSerializeAdapterBinding(getPropertySerializationType(), null).orElse(null)) + .deserializeAdapter(jsonbContext.getComponentMatcher() .getDeserializeAdapterBinding(getPropertyDeserializationType(), null) .orElse(null)); } - introspectDateFormatter(property, introspector, builder, jsonbContext); - introspectNumberFormatter(property, introspector, builder); - builder.implementationClass(introspector.getImplementationClass(property)); - - return builder.build(); + introspectDateFormatter(introspector.getJsonbDateFormatCategorized(property), jsonbContext.getConfigProperties().getConfigDateFormatter(), + builder); + introspectNumberFormatter(introspector.getJsonNumberFormatter(property), builder); + return builder.implementationClass(introspector.getImplementationClass(property)) + .build(); } - private static void introspectDateFormatter(Property property, - AnnotationIntrospector introspector, - PropertyCustomization.Builder builder, - JsonbContext jsonbContext) { + private static void introspectDateFormatter(Map jsonDateFormatCategorized, + final JsonbDateFormatter configDateFormatter, + PropertyCustomization.Builder builder) { /* * If @JsonbDateFormat is placed on getter implementation must use this format on serialization. * If @JsonbDateFormat is placed on setter implementation must use this format on deserialization. @@ -273,31 +273,21 @@ private static void introspectDateFormatter(Property property, * * Priority from high to low is getter / setter > field > class > package > global configuration */ - Map jsonDateFormatCategorized = introspector - .getJsonbDateFormatCategorized(property); - final JsonbDateFormatter configDateFormatter = jsonbContext.getConfigProperties().getConfigDateFormatter(); - - if (!builder.readTransient()) { - final JsonbDateFormatter dateFormatter = getTargetForMostPreciseScope(jsonDateFormatCategorized, + final JsonbDateFormatter dateReadFormatter = getTargetForMostPreciseScope(jsonDateFormatCategorized, AnnotationTarget.GETTER, AnnotationTarget.PROPERTY, AnnotationTarget.CLASS); - builder.serializeDateFormatter(dateFormatter != null ? dateFormatter : configDateFormatter); - } - - if (!builder.writeTransient()) { - final JsonbDateFormatter dateFormatter = getTargetForMostPreciseScope(jsonDateFormatCategorized, - AnnotationTarget.SETTER, - AnnotationTarget.PROPERTY, - AnnotationTarget.CLASS); + final JsonbDateFormatter dateWriteFormatter = getTargetForMostPreciseScope(jsonDateFormatCategorized, + AnnotationTarget.SETTER, + AnnotationTarget.PROPERTY, + AnnotationTarget.CLASS); - builder.deserializeDateFormatter(dateFormatter != null ? dateFormatter : configDateFormatter); - } + builder.serializeDateFormatter(builder.readTransient() ? null : dateReadFormatter != null ? dateReadFormatter : configDateFormatter) + .deserializeDateFormatter(builder.writeTransient() ? null : dateWriteFormatter != null ? dateWriteFormatter : configDateFormatter); } - private static void introspectNumberFormatter(Property property, - AnnotationIntrospector introspector, + private static void introspectNumberFormatter(Map jsonNumberFormatCategorized, PropertyCustomization.Builder builder) { /* * If @JsonbNumberFormat is placed on getter implementation must use this format on serialization. @@ -306,21 +296,16 @@ private static void introspectNumberFormatter(Property property, * * Priority from high to low is getter / setter > field > class > package > global configuration */ - Map jsonNumberFormatCategorized = introspector.getJsonNumberFormatter(property); - - if (!builder.readTransient()) { - builder.serializeNumberFormatter(getTargetForMostPreciseScope(jsonNumberFormatCategorized, + builder.serializeNumberFormatter(builder.readTransient() ? null + : getTargetForMostPreciseScope(jsonNumberFormatCategorized, AnnotationTarget.GETTER, AnnotationTarget.PROPERTY, - AnnotationTarget.CLASS)); - } - - if (!builder.writeTransient()) { - builder.deserializeNumberFormatter(getTargetForMostPreciseScope(jsonNumberFormatCategorized, + AnnotationTarget.CLASS)) + .deserializeNumberFormatter(builder.writeTransient() ? null + : getTargetForMostPreciseScope(jsonNumberFormatCategorized, AnnotationTarget.SETTER, AnnotationTarget.PROPERTY, AnnotationTarget.CLASS)); - } } /** @@ -331,13 +316,10 @@ private static void introspectNumberFormatter(Property property, */ private static T getTargetForMostPreciseScope(Map collectedAnnotations, AnnotationTarget... targets) { - for (AnnotationTarget target : targets) { - final T result = collectedAnnotations.get(target); - if (result != null) { - return result; - } - } - return null; + return Arrays.stream(targets) + .map(collectedAnnotations::get) + .filter(Objects::nonNull) + .findFirst().orElse(null); } /** @@ -356,7 +338,7 @@ public Object getValue(Object object) { /** * Sets a property. - * + *

* If not writable (final, transient, static), ignores property. * * @param object Object to set value in. @@ -376,7 +358,7 @@ public void setValue(Object object, Object value) { /** * Property is readable. Based on access policy and java field modifiers. * - * @return true if can be serialized to JSON + * @return true if property can be serialized to JSON */ public boolean isReadable() { return !customization.isReadTransient() && this.getValueHandle != null; @@ -385,7 +367,7 @@ public boolean isReadable() { /** * Property is writable. Based on access policy and java field modifiers. * - * @return true if can be deserialized from JSON + * @return true if property can be deserialized from JSON */ public boolean isWritable() { return !customization.isWriteTransient() && this.setValueHandle != null; @@ -458,7 +440,7 @@ public String getWriteName() { } /** - * If customized by JsonbPropertyAnnotation, than is used, otherwise use strategy to translate. + * If customized by JsonbPropertyAnnotation, then String is used, otherwise use strategy to translate. * Since this is cached for performance reasons strategy has to be consistent * with calculated values for same input. */ @@ -559,7 +541,7 @@ private static boolean isFieldVisible(Field field, Method method, PropertyVisibi if (field == null) { return false; } - boolean accessible = isVisible(strat -> strat.isVisible(field), method, strategy); + boolean accessible = isVisible(strategy_it -> strategy_it.isVisible(field), method, strategy); //overridden by strategy, or anonymous class (readable by spec) if (accessible && ( !Modifier.isPublic(field.getModifiers()) @@ -579,7 +561,7 @@ private static boolean isMethodVisible(Method method, PropertyVisibilityStrategy return false; } - boolean accessible = isVisible(strat -> strat.isVisible(method), method, strategy); + boolean accessible = isVisible(strategy_it -> strategy_it.isVisible(method), method, strategy); //overridden by strategy, anonymous class, or lambda if (accessible && ( !Modifier.isPublic(method.getModifiers()) || method.getDeclaringClass().isAnonymousClass() || method diff --git a/src/main/java/org/eclipse/yasson/internal/model/ReverseTreeMap.java b/src/main/java/org/eclipse/yasson/internal/model/ReverseTreeMap.java index e6608e9e9..0cffdbd90 100644 --- a/src/main/java/org/eclipse/yasson/internal/model/ReverseTreeMap.java +++ b/src/main/java/org/eclipse/yasson/internal/model/ReverseTreeMap.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024 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 @@ -23,6 +23,8 @@ */ public class ReverseTreeMap, V> extends TreeMap { + private static final long serialVersionUID = 1L; + /** * Default constructor of a TreeMap with reverse order. */ diff --git a/src/main/java/org/eclipse/yasson/internal/model/customization/ComponentBoundCustomization.java b/src/main/java/org/eclipse/yasson/internal/model/customization/ComponentBoundCustomization.java index e964ed805..049b3c4de 100644 --- a/src/main/java/org/eclipse/yasson/internal/model/customization/ComponentBoundCustomization.java +++ b/src/main/java/org/eclipse/yasson/internal/model/customization/ComponentBoundCustomization.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024 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 @@ -24,12 +24,12 @@ public interface ComponentBoundCustomization { /** * @return Adapter wrapper class with resolved generic information. */ - AdapterBinding getSerializeAdapterBinding(); + AdapterBinding getSerializeAdapterBinding(); /** * @return Adapter wrapper class with resolved generic information. */ - AdapterBinding getDeserializeAdapterBinding(); + AdapterBinding getDeserializeAdapterBinding(); /** * Serializer wrapper with resolved generic info. diff --git a/src/main/java/org/eclipse/yasson/internal/model/customization/CustomizationBase.java b/src/main/java/org/eclipse/yasson/internal/model/customization/CustomizationBase.java index 2b49180df..32d962452 100644 --- a/src/main/java/org/eclipse/yasson/internal/model/customization/CustomizationBase.java +++ b/src/main/java/org/eclipse/yasson/internal/model/customization/CustomizationBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024 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 @@ -21,7 +21,7 @@ */ abstract class CustomizationBase implements Customization, ComponentBoundCustomization { - private final AdapterBinding adapterBinding; + private final AdapterBinding adapterBinding; private final SerializerBinding serializerBinding; private final DeserializerBinding deserializerBinding; private final boolean nillable; @@ -47,12 +47,12 @@ public boolean isNillable() { return nillable; } - public AdapterBinding getSerializeAdapterBinding() { + public AdapterBinding getSerializeAdapterBinding() { return adapterBinding; } @Override - public AdapterBinding getDeserializeAdapterBinding() { + public AdapterBinding getDeserializeAdapterBinding() { return adapterBinding; } @@ -77,7 +77,7 @@ public DeserializerBinding getDeserializerBinding() { @SuppressWarnings("unchecked") abstract static class Builder, B extends CustomizationBase> { - private AdapterBinding adapterBinding; + private AdapterBinding adapterBinding; private SerializerBinding serializerBinding; private DeserializerBinding deserializerBinding; private boolean nillable; @@ -93,7 +93,7 @@ public T of(B customization) { return (T) this; } - public T adapterBinding(AdapterBinding adapterBinding) { + public T adapterBinding(AdapterBinding adapterBinding) { this.adapterBinding = adapterBinding; return (T) this; } diff --git a/src/main/java/org/eclipse/yasson/internal/model/customization/PropertyCustomization.java b/src/main/java/org/eclipse/yasson/internal/model/customization/PropertyCustomization.java index 022e458bc..a854cbf30 100644 --- a/src/main/java/org/eclipse/yasson/internal/model/customization/PropertyCustomization.java +++ b/src/main/java/org/eclipse/yasson/internal/model/customization/PropertyCustomization.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024 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 @@ -30,8 +30,8 @@ public class PropertyCustomization extends CustomizationBase { private final JsonbDateFormatter serializeDateFormatter; private final JsonbDateFormatter deserializeDateFormatter; - private final AdapterBinding serializeAdapter; - private final AdapterBinding deserializeAdapter; + private final AdapterBinding serializeAdapter; + private final AdapterBinding deserializeAdapter; private final boolean readTransient; private final boolean writeTransient; @@ -39,7 +39,7 @@ public class PropertyCustomization extends CustomizationBase { private final Class implementationClass; /** - * Copies properties from builder an creates immutable instance. + * Copies properties from builder and creates immutable instance. * * @param builder not null */ @@ -132,12 +132,12 @@ public Class getImplementationClass() { } @Override - public AdapterBinding getDeserializeAdapterBinding() { + public AdapterBinding getDeserializeAdapterBinding() { return deserializeAdapter; } @Override - public AdapterBinding getSerializeAdapterBinding() { + public AdapterBinding getSerializeAdapterBinding() { return serializeAdapter; } @@ -149,8 +149,8 @@ public static final class Builder extends CustomizationBase.Builder serializeAdapter; + private AdapterBinding deserializeAdapter; private boolean readTransient; private boolean writeTransient; private Class implementationClass; @@ -234,12 +234,12 @@ public Builder deserializeDateFormatter(JsonbDateFormatter deserializeDateFormat return this; } - public Builder serializeAdapter(AdapterBinding serializeAdapter) { + public Builder serializeAdapter(AdapterBinding serializeAdapter) { this.serializeAdapter = serializeAdapter; return this; } - public Builder deserializeAdapter(AdapterBinding deserializeAdapter) { + public Builder deserializeAdapter(AdapterBinding deserializeAdapter) { this.deserializeAdapter = deserializeAdapter; return this; } diff --git a/src/main/java/org/eclipse/yasson/internal/model/customization/StrategiesProvider.java b/src/main/java/org/eclipse/yasson/internal/model/customization/StrategiesProvider.java index 4855daad2..2a912fb5b 100644 --- a/src/main/java/org/eclipse/yasson/internal/model/customization/StrategiesProvider.java +++ b/src/main/java/org/eclipse/yasson/internal/model/customization/StrategiesProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024 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 @@ -25,7 +25,6 @@ import org.eclipse.yasson.internal.properties.Messages; import static java.util.Comparator.comparing; - import static jakarta.json.bind.config.PropertyNamingStrategy.CASE_INSENSITIVE; import static jakarta.json.bind.config.PropertyNamingStrategy.IDENTITY; import static jakarta.json.bind.config.PropertyNamingStrategy.LOWER_CASE_WITH_DASHES; diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/AdapterSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/AdapterSerializer.java index 582c581d0..931273bcf 100644 --- a/src/main/java/org/eclipse/yasson/internal/serializer/AdapterSerializer.java +++ b/src/main/java/org/eclipse/yasson/internal/serializer/AdapterSerializer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024 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 @@ -27,13 +27,13 @@ class AdapterSerializer extends AbstractSerializer { private final JsonbAdapter adapter; - private final AdapterBinding adapterBinding; + private final AdapterBinding adapterBinding; @SuppressWarnings("unchecked") - AdapterSerializer(AdapterBinding adapterBinding, + AdapterSerializer(AdapterBinding adapterBinding, ModelSerializer delegate) { super(delegate); - this.adapter = (JsonbAdapter) adapterBinding.getAdapter(); + this.adapter = (JsonbAdapter) adapterBinding.getComponent(); this.adapterBinding = adapterBinding; } @@ -45,7 +45,7 @@ public void serialize(Object value, JsonGenerator generator, SerializationContex throw new JsonbException(Messages.getMessage(MessageKeys.ADAPTER_EXCEPTION, adapterBinding.getBindingType(), adapterBinding.getToType(), - adapter.getClass()), e); + adapterBinding.getComponentClass()), e); } } diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/SerializationModelCreator.java b/src/main/java/org/eclipse/yasson/internal/serializer/SerializationModelCreator.java index 522519b38..a7b7b9be6 100644 --- a/src/main/java/org/eclipse/yasson/internal/serializer/SerializationModelCreator.java +++ b/src/main/java/org/eclipse/yasson/internal/serializer/SerializationModelCreator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024 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 @@ -24,6 +24,7 @@ import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; import jakarta.json.bind.JsonbException; @@ -145,40 +146,23 @@ private ModelSerializer serializerChainInternal(LinkedList chain, if (explicitChain.containsKey(type)) { return explicitChain.get(type); } - Class rawType = ReflectionUtils.getRawType(type); - Optional serializerBinding = userSerializer(type, - (ComponentBoundCustomization) propertyCustomization); - if (serializerBinding.isPresent()) { - return serializerBinding.get(); - } - if (resolveRootAdapter) { - Optional maybeAdapter = adapterBinding(type, (ComponentBoundCustomization) propertyCustomization); - if (maybeAdapter.isPresent()) { - AdapterBinding adapterBinding = maybeAdapter.get(); - Type toType = adapterBinding.getToType(); - Class rawToType = ReflectionUtils.getRawType(toType); - ModelSerializer typeSerializer = TypeSerializers - .getTypeSerializer(rawToType, propertyCustomization, jsonbContext); - if (typeSerializer == null) { - typeSerializer = serializerChain(toType, rootValue, !type.equals(toType)); - } - AdapterSerializer adapterSerializer = new AdapterSerializer(adapterBinding, typeSerializer); - RecursionChecker recursionChecker = new RecursionChecker(adapterSerializer); - NullSerializer nullSerializer = new NullSerializer(recursionChecker, propertyCustomization, jsonbContext); - explicitChain.put(type, nullSerializer); - return nullSerializer; - } + ModelSerializer + serializer = + createUserOrAdapterSerializer(type, propertyCustomization, rootValue, resolveRootAdapter, adapterToType -> !type.equals(adapterToType), + RecursionChecker::new); + if (serializer != null) { + return cacheSerializer(type, serializer); } - ModelSerializer typeSerializer = null; + Class rawType = ReflectionUtils.getRawType(type); if (!Object.class.equals(rawType)) { - typeSerializer = TypeSerializers.getTypeSerializer(chain, rawType, propertyCustomization, jsonbContext, isKey); - } - if (typeSerializer != null) { - if (jsonbContext.getConfigProperties().isStrictIJson() && rootValue) { - throw new JsonbException(Messages.getMessage(MessageKeys.IJSON_ENABLED_SINGLE_VALUE)); + ModelSerializer typeSerializer = TypeSerializers.getTypeSerializer(chain, rawType, propertyCustomization, jsonbContext, isKey); + if (typeSerializer != null) { + if (jsonbContext.getConfigProperties().isStrictIJson() && rootValue) { + throw new JsonbException(Messages.getMessage(MessageKeys.IJSON_ENABLED_SINGLE_VALUE)); + } + return typeSerializer; } - return typeSerializer; } ClassModel classModel = jsonbContext.getMappingContext().getOrCreateClassModel(rawType); if (Collection.class.isAssignableFrom(rawType)) { @@ -219,8 +203,7 @@ private ModelSerializer createObjectSerializer(LinkedList chain, NullVisibilitySwitcher nullVisibilitySwitcher = new NullVisibilitySwitcher(false, keyWriter); NullSerializer nullSerializer = new NullSerializer(nullVisibilitySwitcher, classModel.getClassCustomization(), jsonbContext); - explicitChain.put(type, nullSerializer); - return nullSerializer; + return cacheSerializer(type, nullSerializer); } private void addPolymorphismProperty(TypeInheritanceConfiguration typeInheritanceConfiguration, @@ -313,6 +296,13 @@ private ModelSerializer createArraySerializer(LinkedList chain, Class raw, Customization propertyCustomization) { Class arrayComponent = raw.getComponentType(); + return createArraySerializerInternal(chain, raw, propertyCustomization, arrayComponent); + } + + private ModelSerializer createArraySerializerInternal(LinkedList chain, + Class raw, + Customization propertyCustomization, + Class arrayComponent) { ModelSerializer modelSerializer = memberSerializer(chain, arrayComponent, propertyCustomization, false); ModelSerializer arraySerializer = ArraySerializer.create(raw, jsonbContext, modelSerializer); KeyWriter keyWriter = new KeyWriter(arraySerializer); @@ -325,11 +315,7 @@ private ModelSerializer createGenericArraySerializer(LinkedList chain, Customization propertyCustomization) { Class raw = ReflectionUtils.getRawType(type); Class component = ReflectionUtils.getRawType(((GenericArrayType) type).getGenericComponentType()); - ModelSerializer modelSerializer = memberSerializer(chain, component, propertyCustomization, false); - ModelSerializer arraySerializer = ArraySerializer.create(raw, jsonbContext, modelSerializer); - KeyWriter keyWriter = new KeyWriter(arraySerializer); - NullVisibilitySwitcher nullVisibilitySwitcher = new NullVisibilitySwitcher(true, keyWriter); - return new NullSerializer(nullVisibilitySwitcher, propertyCustomization, jsonbContext); + return createArraySerializerInternal(chain, raw, propertyCustomization, component); } private ModelSerializer createOptionalSerializer(LinkedList chain, @@ -348,28 +334,16 @@ private ModelSerializer memberSerializer(LinkedList chain, Customization customization, boolean key) { Type resolved = ReflectionUtils.resolveType(chain, type); - Class rawType = ReflectionUtils.getRawType(resolved); - Optional serializerBinding = userSerializer(resolved, - (ComponentBoundCustomization) customization); - if (serializerBinding.isPresent()) { - return serializerBinding.get(); - } - Optional maybeAdapter = adapterBinding(resolved, (ComponentBoundCustomization) customization); - if (maybeAdapter.isPresent()) { - AdapterBinding adapterBinding = maybeAdapter.get(); - Type toType = adapterBinding.getToType(); - Class rawToType = ReflectionUtils.getRawType(toType); - ModelSerializer typeSerializer = TypeSerializers.getTypeSerializer(rawToType, customization, jsonbContext); - if (typeSerializer == null) { - typeSerializer = serializerChain(toType, false, true); - } - AdapterSerializer adapterSerializer = new AdapterSerializer(adapterBinding, typeSerializer); - return new NullSerializer(adapterSerializer, customization, jsonbContext); + ModelSerializer serializer = + createUserOrAdapterSerializer(resolved, customization, false, true, adapterToType -> true, modelSerializer -> modelSerializer); + if (serializer != null) { + return serializer; } + Class rawType = ReflectionUtils.getRawType(resolved); ModelSerializer typeSerializer = TypeSerializers.getTypeSerializer(chain, rawType, customization, jsonbContext, key); if (typeSerializer == null) { - //Final classes dont have any child classes. It is safe to assume that there will be instance of that specific class. + //Final classes don't have any child classes. It is safe to assume that there will be instance of that specific class. boolean isFinal = Modifier.isFinal(rawType.getModifiers()); if (isFinal || Collection.class.isAssignableFrom(rawType) @@ -413,19 +387,47 @@ private ModelSerializer memberSerializer(LinkedList chain, return typeSerializer; } - private Optional userSerializer(Type type, ComponentBoundCustomization classCustomization) { + /** + * Create a serializer from serializer's or adapter's description in the given type. + * + * @param type type to find the information about serializer or adapter + * @param customization customization witch probably has information about serializer or adapter + * @param rootValue is this a root value + * @param resolveRootAdapter should we resolve root adapter + * @param resolveRootAdapterFunction function to determine if we should resolve root adapter in recursive call; parameter is the type in which adapter should map to + * @param wrapAdapterSerializerFunction function to wrap adapter serializer with model serializer if needed + * @return serializer for the given type or alternative serializer if no serializer or adapter was configured for the class or property + */ + private ModelSerializer createUserOrAdapterSerializer(Type type, Customization customization, boolean rootValue, + boolean resolveRootAdapter, + Function resolveRootAdapterFunction, + Function wrapAdapterSerializerFunction) { + final ComponentMatcher componentMatcher = jsonbContext.getComponentMatcher(); - return componentMatcher.getSerializerBinding(type, classCustomization) - .map(SerializerBinding::getJsonbSerializer) + return componentMatcher.getSerializerBinding(type, (ComponentBoundCustomization) customization) + .map(SerializerBinding::getComponent) .map(UserDefinedSerializer::new) .map(RecursionChecker::new) - .map(serializer -> SerializationModelCreator.wrapInCommonSet(serializer, - (Customization) classCustomization, - jsonbContext)); + .map(serializer -> SerializationModelCreator.wrapInCommonSet(serializer, customization, jsonbContext)) + .orElseGet(() -> { + Optional> adapterBinding = componentMatcher.getSerializeAdapterBinding(type, (ComponentBoundCustomization) customization); + return resolveRootAdapter ? adapterBinding.map(binding -> { + Type toType = binding.getToType(); + Class rawToType = ReflectionUtils.getRawType(toType); + ModelSerializer typeSerializer = TypeSerializers.getTypeSerializer(rawToType, customization, jsonbContext); + if (typeSerializer == null) { + typeSerializer = serializerChain(toType, rootValue, resolveRootAdapterFunction.apply(toType)); + } + AdapterSerializer adapterSerializer = new AdapterSerializer(binding, typeSerializer); + ModelSerializer wrappedAdapterSerializer = wrapAdapterSerializerFunction.apply(adapterSerializer); + return new NullSerializer(wrappedAdapterSerializer, customization, jsonbContext); + }).orElse(null) + : null; + }); } - private Optional adapterBinding(Type type, ComponentBoundCustomization classCustomization) { - return jsonbContext.getComponentMatcher().getSerializeAdapterBinding(type, classCustomization); + private ModelSerializer cacheSerializer(Type type, ModelSerializer serializer) { + explicitChain.put(type, serializer); + return serializer; } - -} +} \ No newline at end of file diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/EnumSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/EnumSerializer.java index 39b14ae1f..fd8c61412 100644 --- a/src/main/java/org/eclipse/yasson/internal/serializer/types/EnumSerializer.java +++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/EnumSerializer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024 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 @@ -12,26 +12,57 @@ package org.eclipse.yasson.internal.serializer.types; +import java.util.EnumMap; + import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.internal.SerializationContextImpl; +import org.eclipse.yasson.internal.model.ClassModel; +import org.eclipse.yasson.internal.model.PropertyModel; + /** * Serializer of the {@link Enum} types. */ class EnumSerializer extends TypeSerializer> { + private final EnumMap, String> constantToNameMap; + EnumSerializer(TypeSerializerBuilder serializerBuilder) { super(serializerBuilder); + + constantToNameMap = createConstantToNameMap(serializerBuilder); + } + + private static > EnumMap createConstantToNameMap(TypeSerializerBuilder serializerBuilder) { + EnumMap constantToNameMap = null; + Class clazz = serializerBuilder.getClazz(); + + if (clazz.isEnum()) { + try { + @SuppressWarnings("unchecked") + Class enumClazz = (Class) clazz; + constantToNameMap = new EnumMap<>(enumClazz); + ClassModel classModel = serializerBuilder.getJsonbContext().getMappingContext().getOrCreateClassModel(clazz); + + for (E enumConstant : enumClazz.getEnumConstants()) { + PropertyModel model = classModel.getPropertyModel(enumConstant.name()); + constantToNameMap.put(enumConstant, model.getWriteName()); + } + } catch (ClassCastException classCastException) { + throw new IllegalArgumentException("EnumSerializer can only be used with Enum types"); + } + } + return constantToNameMap; } @Override void serializeValue(Enum value, JsonGenerator generator, SerializationContextImpl context) { - generator.write(value.name()); + generator.write(constantToNameMap == null ? value.name() : constantToNameMap.get(value)); } @Override void serializeKey(Enum key, JsonGenerator generator, SerializationContextImpl context) { - generator.writeKey(key.name()); + generator.writeKey(constantToNameMap == null ? key.name() : constantToNameMap.get(key)); } } diff --git a/src/test/java/org/eclipse/yasson/Assertions.java b/src/test/java/org/eclipse/yasson/Assertions.java index 8b23d7cbd..390520b71 100644 --- a/src/test/java/org/eclipse/yasson/Assertions.java +++ b/src/test/java/org/eclipse/yasson/Assertions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024 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 @@ -20,6 +20,9 @@ import jakarta.json.bind.JsonbException; public class Assertions { + + private Assertions() { + } /** * Asserts that the given operation will fail with a JsonbException @@ -58,13 +61,13 @@ public static void shouldFail(Supplier operation, Class operation.get(); fail("The operation should have failed with a " + expectedType.getCanonicalName() + " but it succeeded."); } catch (Throwable t) { - String fullErrorMessage = ""; + StringBuilder fullErrorMessage = new StringBuilder(); for (Throwable current = t; current != null && current.getCause() != current; current = current.getCause()) { - fullErrorMessage += current.getClass().getCanonicalName() + ": "; - fullErrorMessage += current.getMessage() + "\n"; + fullErrorMessage.append(current.getClass().getCanonicalName()).append(": "); + fullErrorMessage.append(current.getMessage()).append("\n"); } if (expectedType.isAssignableFrom(t.getClass())) { - if (!checkExceptionMessage.apply(fullErrorMessage)) { + if (!checkExceptionMessage.apply(fullErrorMessage.toString())) { t.printStackTrace(); fail("Exception did not contain the proper content: " + fullErrorMessage); } diff --git a/src/test/java/org/eclipse/yasson/DefaultGetterInInterface.java b/src/test/java/org/eclipse/yasson/DefaultGetterInInterface.java index 9b5954edd..91b50284a 100644 --- a/src/test/java/org/eclipse/yasson/DefaultGetterInInterface.java +++ b/src/test/java/org/eclipse/yasson/DefaultGetterInInterface.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024 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 @@ -27,16 +27,21 @@ * * @author Maxence Laurent */ -public class DefaultGetterInInterface { +class DefaultGetterInInterface { - public static interface Defaulted { + private DefaultGetterInInterface() { + } + + public interface Defaulted { - default public String getGetterA() { + default String getGetterA() { return "valueA"; } } public static class PojoWithDefault implements Defaulted { + protected PojoWithDefault() { + } } @Test @@ -46,13 +51,13 @@ public void testWithDefault() { assertEquals("{\"getterA\":\"valueA\"}", result); } - public static interface WithGetterI { + public interface WithGetterI { @JsonbProperty("withGetterI") String getGetterI(); } - public static interface WithDefaultGetterI extends WithGetterI { + public interface WithDefaultGetterI extends WithGetterI { @Override @JsonbProperty("default") @@ -61,7 +66,7 @@ default String getGetterI() { } } - public static interface OtherWithDefaultGetterI extends WithGetterI { + public interface OtherWithDefaultGetterI extends WithGetterI { @Override @JsonbProperty("otherDefault") @@ -72,6 +77,9 @@ default String getGetterI() { public static class Pojo implements WithGetterI { + protected Pojo() { + } + @Override @JsonbProperty("implementation") public String getGetterI() { @@ -82,6 +90,9 @@ public String getGetterI() { public static class PojoNoAnnotation implements WithGetterI { + protected PojoNoAnnotation() { + } + @Override public String getGetterI() { return "withGetterI"; @@ -89,10 +100,15 @@ public String getGetterI() { } public static class PojoWithDefaultSuperImplementation extends Pojo implements WithDefaultGetterI { + protected PojoWithDefaultSuperImplementation() { + } } public static class PojoWithDefaultImplementation implements WithDefaultGetterI { + protected PojoWithDefaultImplementation() { + } + @Override @JsonbProperty("defaultImplementation") public String getGetterI() { @@ -102,9 +118,13 @@ public String getGetterI() { } public static class PojoWithDefaultOnly implements WithDefaultGetterI { + protected PojoWithDefaultOnly() { + } } public static class PojoGetterDefaultedTwice extends PojoWithDefaultImplementation implements OtherWithDefaultGetterI { + protected PojoGetterDefaultedTwice() { + } } @Test diff --git a/src/test/java/org/eclipse/yasson/FieldAccessStrategyTest.java b/src/test/java/org/eclipse/yasson/FieldAccessStrategyTest.java index 1f04f1490..cfa90b425 100644 --- a/src/test/java/org/eclipse/yasson/FieldAccessStrategyTest.java +++ b/src/test/java/org/eclipse/yasson/FieldAccessStrategyTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024 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 @@ -8,20 +8,24 @@ * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause - */package org.eclipse.yasson; + */ +package org.eclipse.yasson; import org.junit.jupiter.api.*; + + import static org.eclipse.yasson.Jsonbs.testWithJsonbBuilderCreate; import static org.junit.jupiter.api.Assertions.*; -import jakarta.json.bind.Jsonb; -import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.annotation.JsonbTransient; import jakarta.json.bind.config.PropertyVisibilityStrategy; import java.lang.reflect.Field; import java.lang.reflect.Method; -public class FieldAccessStrategyTest { +class FieldAccessStrategyTest { + + private FieldAccessStrategyTest() { + } public static class PrivateFields { private String strField; @@ -49,38 +53,44 @@ public void setStrField(String strField) { } public static class PublicFields { + + protected PublicFields() { + } + public String strField; } @Test public void testPrivateFields() { - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyVisibilityStrategy(new FieldAccessStrategy())); + testWithJsonbBuilderCreate(new JsonbConfig().withPropertyVisibilityStrategy(new FieldAccessStrategy()), jsonb -> { - PrivateFields pojo = new PrivateFields("pojo string"); + PrivateFields pojo = new PrivateFields("pojo string"); - String expected = "{\"strField\":\"pojo string\"}"; + String expected = "{\"strField\":\"pojo string\"}"; - assertEquals(expected, jsonb.toJson(pojo)); - PrivateFields result = jsonb.fromJson(expected, PrivateFields.class); - assertEquals(false, result.getterCalled); - assertEquals(false, result.setterCalled); - assertEquals("pojo string", result.strField); + assertEquals(expected, jsonb.toJson(pojo)); + PrivateFields result = jsonb.fromJson(expected, PrivateFields.class); + assertFalse(result.getterCalled); + assertFalse(result.setterCalled); + assertEquals("pojo string", result.strField); + }); } @Test public void testHidePublicFields() { - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyVisibilityStrategy(new NoAccessStrategy())); + testWithJsonbBuilderCreate(new JsonbConfig().withPropertyVisibilityStrategy(new NoAccessStrategy()), jsonb -> { - PublicFields pojo = new PublicFields(); - pojo.strField = "string field"; + PublicFields pojo = new PublicFields(); + pojo.strField = "string field"; - String expected = "{}"; + String expected = "{}"; - assertEquals(expected, jsonb.toJson(pojo)); - PublicFields result = jsonb.fromJson("{\"strField\":\"pojo string\"}", PublicFields.class); - assertEquals(null, result.strField); + assertEquals(expected, jsonb.toJson(pojo)); + PublicFields result = jsonb.fromJson("{\"strField\":\"pojo string\"}", PublicFields.class); + assertNull(result.strField); + }); } /** @@ -88,23 +98,28 @@ public void testHidePublicFields() { */ @Test public void testCustomVisibityStrategy() { - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyVisibilityStrategy(new CustomVisibilityStrategy())); - - String json = "{\"floatInstance\":10.0,\"stringInstance\":\"Test String\"}"; - SimpleContainer simpleContainer = new SimpleContainer(); - simpleContainer.setStringInstance("Test String"); - simpleContainer.setIntegerInstance(10); - simpleContainer.setFloatInstance(10.0f); - assertEquals(json, jsonb.toJson(simpleContainer)); - - - SimpleContainer result = jsonb.fromJson("{ \"stringInstance\" : \"Test String\", \"floatInstance\" : 1.0, \"integerInstance\" : 1 }", SimpleContainer.class); - assertEquals("Test String", result.stringInstance); - assertNull(result.integerInstance); - assertNull(result.floatInstance); + testWithJsonbBuilderCreate(new JsonbConfig().withPropertyVisibilityStrategy(new CustomVisibilityStrategy()), jsonb -> { + + String json = "{\"floatInstance\":10.0,\"stringInstance\":\"Test String\"}"; + SimpleContainer simpleContainer = new SimpleContainer(); + simpleContainer.setStringInstance("Test String"); + simpleContainer.setIntegerInstance(10); + simpleContainer.setFloatInstance(10.0f); + assertEquals(json, jsonb.toJson(simpleContainer)); + + + SimpleContainer result = jsonb.fromJson("{ \"stringInstance\" : \"Test String\", \"floatInstance\" : 1.0, \"integerInstance\" : 1 }", + SimpleContainer.class); + assertEquals("Test String", result.stringInstance); + assertNull(result.integerInstance); + assertNull(result.floatInstance); + }); } - public class CustomVisibilityStrategy implements PropertyVisibilityStrategy { + public static class CustomVisibilityStrategy implements PropertyVisibilityStrategy { + public CustomVisibilityStrategy() { + } + @Override public boolean isVisible(Field field) { return field.getName().equals("stringInstance"); @@ -115,7 +130,11 @@ public boolean isVisible(Method method) { return method.getName().equals("getFloatInstance"); } } + public static class SimpleContainer { + protected SimpleContainer() { + } + private String stringInstance; private Integer integerInstance; private Float floatInstance; @@ -148,6 +167,9 @@ public void setFloatInstance(float floatInstance) { private static final class NoAccessStrategy implements PropertyVisibilityStrategy { + public NoAccessStrategy() { + } + @Override public boolean isVisible(Field field) { return false; diff --git a/src/test/java/org/eclipse/yasson/Issue454Test.java b/src/test/java/org/eclipse/yasson/Issue454Test.java index fa2363a51..5ebb249b1 100644 --- a/src/test/java/org/eclipse/yasson/Issue454Test.java +++ b/src/test/java/org/eclipse/yasson/Issue454Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024 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 @@ -12,46 +12,57 @@ package org.eclipse.yasson; +import static org.eclipse.yasson.Jsonbs.testWithJsonbBuilderCreate; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; -import jakarta.json.bind.Jsonb; -import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.annotation.JsonbTransient; -public class Issue454Test { +class Issue454Test { + + private Issue454Test() { + } @Test public void test() { final String EXPECTED = "{\"field2\":\"bbb\"}"; - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()); - assertEquals(EXPECTED, jsonb.toJson(new TheInterface() { - - @Override - public String getField1() { - return "aaa"; - } - - @Override - public String getField2() { - return "bbb"; - }})); - assertEquals(EXPECTED, jsonb.toJson(new TheClass() { - @Override - public String getField1() { - return "aaa"; - } - @Override - public String getField2() { - return "bbb"; - }})); - assertEquals(EXPECTED, jsonb.toJson(new TheClass2())); - assertEquals(EXPECTED, jsonb.toJson(new TheClass2() {})); + testWithJsonbBuilderCreate(new JsonbConfig(), jsonb -> { + assertEquals(EXPECTED, jsonb.toJson(new TheInterface() { + + @Override + public String getField1() { + return "aaa"; + } + + @Override + public String getField2() { + return "bbb"; + } + })); + assertEquals(EXPECTED, jsonb.toJson(new TheClass() { + @Override + public String getField1() { + return "aaa"; + } + + @Override + public String getField2() { + return "bbb"; + } + })); + assertEquals(EXPECTED, jsonb.toJson(new TheClass2())); + assertEquals(EXPECTED, jsonb.toJson(new TheClass2() { + })); + }); } public static abstract class TheClass { + + private TheClass() { + } + @JsonbTransient public abstract String getField1(); @@ -59,6 +70,10 @@ public static abstract class TheClass { } public static class TheClass2 extends TheClass { + + private TheClass2() { + } + @Override public String getField1() { return "aaa"; @@ -69,7 +84,7 @@ public String getField2() { } } - public static interface TheInterface { + public interface TheInterface { @JsonbTransient String getField1(); diff --git a/src/test/java/org/eclipse/yasson/Issue456Test.java b/src/test/java/org/eclipse/yasson/Issue456Test.java index 4bb4f89ef..7e215bf24 100644 --- a/src/test/java/org/eclipse/yasson/Issue456Test.java +++ b/src/test/java/org/eclipse/yasson/Issue456Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024 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 @@ -12,19 +12,22 @@ package org.eclipse.yasson; +import static org.eclipse.yasson.Jsonbs.defaultJsonb; import static org.junit.jupiter.api.Assertions.fail; import org.junit.jupiter.api.Test; -import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbException; -public class Issue456Test { +class Issue456Test { + + private Issue456Test() { + } @Test public void dontInvokeToString() { try { - JsonbBuilder.create().toJson(new Example()); + defaultJsonb.toJson(new Example()); fail("JsonbException is expected"); } catch (JsonbException e) { // Expected @@ -33,13 +36,16 @@ public void dontInvokeToString() { public static class Example { + protected Example() { + } + public String getProperty() { throw new RuntimeException("some error"); } @Override public String toString() { - return JsonbBuilder.create().toJson(this); + return defaultJsonb.toJson(this); } } } diff --git a/src/test/java/org/eclipse/yasson/JavaxNamingExcludedTest.java b/src/test/java/org/eclipse/yasson/JavaxNamingExcludedTest.java index dac6960d1..ea8d69b4e 100644 --- a/src/test/java/org/eclipse/yasson/JavaxNamingExcludedTest.java +++ b/src/test/java/org/eclipse/yasson/JavaxNamingExcludedTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024 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 @@ -25,7 +25,10 @@ * Requires --limit-modules java.base,java.logging,java.sql (to exclude java.naming) to work. * See pom.xml surefire plugin configuration. */ -public class JavaxNamingExcludedTest { +class JavaxNamingExcludedTest { + + private JavaxNamingExcludedTest() { + } @Test public void testNoJavaxNamingModule() { @@ -42,6 +45,10 @@ public void testNoJavaxNamingModule() { } public static final class AdaptedPojo { + + private AdaptedPojo() { + } + @JsonbTypeAdapter(NonCdiAdapter.class) public String adaptedValue1 = "1111"; diff --git a/src/test/java/org/eclipse/yasson/Jsonbs.java b/src/test/java/org/eclipse/yasson/Jsonbs.java index 2640e7da7..0b69369fe 100644 --- a/src/test/java/org/eclipse/yasson/Jsonbs.java +++ b/src/test/java/org/eclipse/yasson/Jsonbs.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024 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 @@ -12,6 +12,9 @@ package org.eclipse.yasson; +import java.util.function.Consumer; +import java.util.function.Supplier; + import jakarta.json.bind.*; import org.eclipse.yasson.internal.*; @@ -22,4 +25,32 @@ public class Jsonbs { public static final Jsonb nullableJsonb = JsonbBuilder.create(new JsonbConfig().withNullValues(Boolean.TRUE)); public static final YassonJsonb yassonJsonb = (YassonJsonb) JsonbBuilder.create(); public static final YassonJsonb bindingYassonJsonb = (YassonJsonb) new JsonBindingProvider().create().build(); + + private static void testWithJsonb(Supplier supplier, Consumer consumer){ + try (Jsonb jsonb = supplier.get()) { + consumer.accept(jsonb); + } catch (InterruptedException ie) { + throw new RuntimeException("InterruptedException was thrown", ie); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static void testWithJsonbBuilderNewBuilder(JsonbConfig jsonbConfig, Consumer consumer){ + testWithJsonb(() -> jsonbConfig == null ? JsonbBuilder.newBuilder().build() : JsonbBuilder.newBuilder().withConfig(jsonbConfig).build(), consumer); + } + + public static void testWithJsonbBuilderNewBuilder(Consumer consumer){ + testWithJsonbBuilderNewBuilder(null, consumer); + } + + public static void testWithJsonbBuilderCreate(JsonbConfig jsonbConfig, Consumer consumer){ + testWithJsonb(() -> jsonbConfig == null ? JsonbBuilder.create() : JsonbBuilder.create(jsonbConfig), consumer); + } + + public static void testWithJsonbBuilderCreate(Consumer consumer) { + testWithJsonbBuilderCreate(null, consumer); + } + + private Jsonbs() {} } diff --git a/src/test/java/org/eclipse/yasson/SimpleTest.java b/src/test/java/org/eclipse/yasson/SimpleTest.java index ca11319f6..58e29335a 100644 --- a/src/test/java/org/eclipse/yasson/SimpleTest.java +++ b/src/test/java/org/eclipse/yasson/SimpleTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024 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 @@ -21,7 +21,10 @@ /** * @author Roman Grigoriadi */ -public class SimpleTest { +class SimpleTest { + + private SimpleTest() { + } @Test public void testSimpleSerialize() { @@ -39,6 +42,9 @@ public void testSimpleDeserializer() { public static class StringWrapper { + protected StringWrapper() { + } + public String value; public String getValue() { diff --git a/src/test/java/org/eclipse/yasson/TestTypeToken.java b/src/test/java/org/eclipse/yasson/TestTypeToken.java index 6c6c458d1..9c31a7c8f 100644 --- a/src/test/java/org/eclipse/yasson/TestTypeToken.java +++ b/src/test/java/org/eclipse/yasson/TestTypeToken.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024 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 @@ -17,8 +17,13 @@ /** * @author Roman Grigoriadi + * @param Type of token */ -public abstract class TestTypeToken { +public abstract class TestTypeToken { // we need T here so getType() returns correct type + + protected TestTypeToken() { + } + public Type getType() { return ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; } diff --git a/src/test/java/org/eclipse/yasson/YassonConfigTest.java b/src/test/java/org/eclipse/yasson/YassonConfigTest.java index fc5eb0f8b..2c93db709 100644 --- a/src/test/java/org/eclipse/yasson/YassonConfigTest.java +++ b/src/test/java/org/eclipse/yasson/YassonConfigTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024 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 @@ -14,15 +14,14 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -import jakarta.json.bind.Jsonb; -import jakarta.json.bind.JsonbBuilder; - import org.junit.jupiter.api.Test; /** * Tests that the names of configuration fields in {@link YassonConfig} do not change. */ -public class YassonConfigTest { +class YassonConfigTest { + + private YassonConfigTest() {} @SuppressWarnings("deprecation") @Test diff --git a/src/test/java/org/eclipse/yasson/adapters/AdaptersTest.java b/src/test/java/org/eclipse/yasson/adapters/AdaptersTest.java index d3db6c945..807710f5a 100644 --- a/src/test/java/org/eclipse/yasson/adapters/AdaptersTest.java +++ b/src/test/java/org/eclipse/yasson/adapters/AdaptersTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024 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 @@ -12,21 +12,16 @@ package org.eclipse.yasson.adapters; -import org.junit.jupiter.api.*; -import static org.junit.jupiter.api.Assertions.*; -import static org.eclipse.yasson.Jsonbs.*; +import static java.util.Collections.unmodifiableMap; -import org.eclipse.yasson.TestTypeToken; -import org.eclipse.yasson.adapters.model.*; -import org.eclipse.yasson.defaultmapping.generics.model.ScalarValueWrapper; +import static org.eclipse.yasson.Jsonbs.defaultJsonb; +import static org.eclipse.yasson.Jsonbs.testWithJsonbBuilderNewBuilder; +import static org.eclipse.yasson.Jsonbs.testWithJsonbBuilderCreate; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; -import jakarta.json.Json; -import jakarta.json.JsonObject; -import jakarta.json.JsonString; -import jakarta.json.bind.Jsonb; -import jakarta.json.bind.JsonbBuilder; -import jakarta.json.bind.JsonbConfig; -import jakarta.json.bind.adapter.JsonbAdapter; import java.lang.reflect.Type; import java.math.BigDecimal; import java.time.Instant; @@ -38,7 +33,27 @@ import java.util.Optional; import java.util.UUID; -import static java.util.Collections.unmodifiableMap; +import org.eclipse.yasson.TestTypeToken; +import org.eclipse.yasson.adapters.model.AdaptedPojo; +import org.eclipse.yasson.adapters.model.Author; +import org.eclipse.yasson.adapters.model.Box; +import org.eclipse.yasson.adapters.model.BoxToCrateCompatibleGenericsAdapter; +import org.eclipse.yasson.adapters.model.BoxToCratePropagatedIntegerStringAdapter; +import org.eclipse.yasson.adapters.model.Crate; +import org.eclipse.yasson.adapters.model.GenericBox; +import org.eclipse.yasson.adapters.model.IntegerListToStringAdapter; +import org.eclipse.yasson.adapters.model.JsonObjectPojo; +import org.eclipse.yasson.adapters.model.NumberAdapter; +import org.eclipse.yasson.adapters.model.ReturnNullAdapter; +import org.eclipse.yasson.adapters.model.SupertypeAdapterPojo; +import org.eclipse.yasson.adapters.model.UUIDContainer; +import org.eclipse.yasson.adapters.model.Vegetables; +import org.eclipse.yasson.defaultmapping.generics.model.ScalarValueWrapper; +import org.junit.jupiter.api.Test; + +import jakarta.json.bind.JsonbConfig; +import jakarta.json.bind.JsonbException; +import jakarta.json.bind.adapter.JsonbAdapter; /** * Tests adapters to behave correctly. @@ -53,7 +68,7 @@ public static class NonGenericPojo { } @Test - public void testBoxToCrateNoGenerics() throws Exception { + public void testBoxToCrateNoGenerics() { JsonbAdapter[] adapters = { new JsonbAdapter() { @Override @@ -73,23 +88,23 @@ public Box adaptFromJson(Crate crate) { } } }; - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters)); - - AdaptedPojo pojo = new AdaptedPojo(); - Box box = new Box(); - box.setBoxStrField("BoxStr"); - box.setBoxIntegerField(10); - pojo.box = box; - String json = jsonb.toJson(pojo); - assertEquals("{\"box\":{\"crateIntField\":11,\"crateStrField\":\"crateAdaptedBoxStr\"}}", json); - - AdaptedPojo result = jsonb.fromJson("{\"box\":{\"crateIntField\":10,\"crateStrField\":\"CrateStr\"}}", AdaptedPojo.class); - assertEquals(Integer.valueOf(11), result.box.getBoxIntegerField()); - assertEquals("boxAdaptedCrateStr", result.box.getBoxStrField()); + testWithJsonbBuilderCreate(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters), jsonb -> { + AdaptedPojo pojo = new AdaptedPojo<>(); + Box box = new Box(); + box.setBoxStrField("BoxStr"); + box.setBoxIntegerField(10); + pojo.box = box; + String json = jsonb.toJson(pojo); + assertEquals("{\"box\":{\"crateIntField\":11,\"crateStrField\":\"crateAdaptedBoxStr\"}}", json); + + AdaptedPojo result = jsonb.fromJson("{\"box\":{\"crateIntField\":10,\"crateStrField\":\"CrateStr\"}}", AdaptedPojo.class); + assertEquals(Integer.valueOf(11), result.box.getBoxIntegerField()); + assertEquals("boxAdaptedCrateStr", result.box.getBoxStrField()); + }); } @Test - public void testValueFieldAdapter() throws Exception { + public void testValueFieldAdapter() { JsonbAdapter[] adapters = { new JsonbAdapter() { @Override @@ -103,130 +118,145 @@ public Integer adaptFromJson(String s) { } } }; - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters)); + testWithJsonbBuilderCreate(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters), jsonb -> { - AdaptedPojo pojo = new AdaptedPojo(); - pojo.intField = 11; - String json = jsonb.toJson(pojo); - assertEquals("{\"intField\":\"11\"}", json); + AdaptedPojo pojo = new AdaptedPojo<>(); + pojo.intField = 11; + String json = jsonb.toJson(pojo); + assertEquals("{\"intField\":\"11\"}", json); - AdaptedPojo result = jsonb.fromJson("{\"intField\":\"10\"}", AdaptedPojo.class); - assertEquals(Integer.valueOf(10), result.intField); + AdaptedPojo result = jsonb.fromJson("{\"intField\":\"10\"}", AdaptedPojo.class); + assertEquals(Integer.valueOf(10), result.intField); + }); } @Test - public void testGenericAdapter() throws Exception { + public void testGenericAdapter() { JsonbAdapter[] adapters = {new BoxToCrateCompatibleGenericsAdapter() { }}; - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters)); - - AdaptedPojo pojo = new AdaptedPojo<>(); - pojo.strField = "POJO_STRING"; - pojo.intBox = new GenericBox<>("INT_BOX_STR", 11); - pojo.tBox = new GenericBox<>("T_BOX_STR", 110); - - String marshalledJson = jsonb.toJson(pojo, new TestTypeToken>(){}.getType()); - assertEquals("{\"intBox\":{\"adaptedT\":11,\"crateStrField\":\"INT_BOX_STR\"}," + - "\"strField\":\"POJO_STRING\"," + - "\"tBox\":{\"adaptedT\":110,\"crateStrField\":\"T_BOX_STR\"}}", marshalledJson); - - String toUnmarshall = "{\"intBox\":{\"crateStrField\":\"Box3\",\"adaptedT\":33}," + - "\"tBox\":{\"crateStrField\":\"tGenBoxCrateStr\",\"adaptedT\":22}," + - "\"strField\":\"POJO_STRING\"," + - "\"strBox\":{\"strField\":\"strBoxStr\",\"x\":\"44\"}}"; - AdaptedPojo result = jsonb.fromJson(toUnmarshall, new TestTypeToken>(){}.getType()); - assertEquals("POJO_STRING", result.strField); - assertEquals("Box3", result.intBox.getStrField()); - assertEquals(33, result.intBox.getX()); - assertEquals("tGenBoxCrateStr", result.tBox.getStrField()); - assertEquals(22, result.tBox.getX()); - assertEquals("strBoxStr", result.strBox.getStrField()); - assertEquals("44", result.strBox.getX()); + testWithJsonbBuilderCreate(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters), jsonb -> { + + AdaptedPojo pojo = new AdaptedPojo<>(); + pojo.strField = "POJO_STRING"; + pojo.intBox = new GenericBox<>("INT_BOX_STR", 11); + pojo.tBox = new GenericBox<>("T_BOX_STR", 110); + + String marshalledJson = jsonb.toJson(pojo, new TestTypeToken>() { + }.getType()); + assertEquals("{\"intBox\":{\"adaptedT\":11,\"crateStrField\":\"INT_BOX_STR\"}," + + "\"strField\":\"POJO_STRING\"," + + "\"tBox\":{\"adaptedT\":110,\"crateStrField\":\"T_BOX_STR\"}}", marshalledJson); + + String toUnmarshall = "{\"intBox\":{\"crateStrField\":\"Box3\",\"adaptedT\":33}," + + "\"tBox\":{\"crateStrField\":\"tGenBoxCrateStr\",\"adaptedT\":22}," + + "\"strField\":\"POJO_STRING\"," + + "\"strBox\":{\"strField\":\"strBoxStr\",\"x\":\"44\"}}"; + AdaptedPojo result = jsonb.fromJson(toUnmarshall, new TestTypeToken>() { + }.getType()); + assertEquals("POJO_STRING", result.strField); + assertEquals("Box3", result.intBox.getStrField()); + assertEquals(33, result.intBox.getX()); + assertEquals("tGenBoxCrateStr", result.tBox.getStrField()); + assertEquals(22, result.tBox.getX()); + assertEquals("strBoxStr", result.strBox.getStrField()); + assertEquals("44", result.strBox.getX()); + }); } @Test - public void testPropagatedTypeArgs() throws Exception { + public void testPropagatedTypeArgs() { JsonbAdapter[] adapters = {new BoxToCratePropagatedIntegerStringAdapter()}; - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters)); - - AdaptedPojo pojo = new AdaptedPojo<>(); - pojo.intBox = new GenericBox<>("INT_BOX_STR", 110); - pojo.tBox = new GenericBox<>("T_BOX_STR", 111); - pojo.strBox = new GenericBox<>("STR_BOX_STR", "101"); - - String marshalledJson = jsonb.toJson(pojo, new TestTypeToken>(){}.getType()); - assertEquals("{\"intBox\":{\"adaptedT\":{\"x\":[\"110\"]},\"crateStrField\":\"INT_BOX_STR\"}," + - "\"strBox\":{\"strField\":\"STR_BOX_STR\",\"x\":\"101\"}," + - "\"tBox\":{\"adaptedT\":{\"x\":[\"111\"]},\"crateStrField\":\"T_BOX_STR\"}}", - marshalledJson); - - String toUnmarshall = "{\"intBox\":{\"crateStrField\":\"strCrateStr\",\"adaptedT\":{\"strField\":\"crateBoxStrField\",\"x\":[\"77\"]}}," + - "\"tBox\":{\"crateStrField\":\"tStrCrateStr\",\"adaptedT\":{\"strField\":\"crateBoxStrField\",\"x\":[\"88\"]}}," + - "\"strField\":\"POJO_STRING\"," + - "\"strBox\":{\"strField\":\"strBoxStr\",\"x\":\"44\"}}"; - - AdaptedPojo result = jsonb.fromJson(toUnmarshall, new TestTypeToken>(){}.getType()); - assertEquals("POJO_STRING", result.strField); - assertEquals("strCrateStr", result.intBox.getStrField()); - assertEquals(77, result.intBox.getX()); - assertEquals("tStrCrateStr", result.tBox.getStrField()); - assertEquals(88, result.tBox.getX()); - assertEquals("strBoxStr", result.strBox.getStrField()); - assertEquals("44", result.strBox.getX()); + testWithJsonbBuilderCreate(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters), jsonb -> { + + AdaptedPojo pojo = new AdaptedPojo<>(); + pojo.intBox = new GenericBox<>("INT_BOX_STR", 110); + pojo.tBox = new GenericBox<>("T_BOX_STR", 111); + pojo.strBox = new GenericBox<>("STR_BOX_STR", "101"); + + String marshalledJson = jsonb.toJson(pojo, new TestTypeToken>() { + }.getType()); + assertEquals("{\"intBox\":{\"adaptedT\":{\"x\":[\"110\"]},\"crateStrField\":\"INT_BOX_STR\"}," + + "\"strBox\":{\"strField\":\"STR_BOX_STR\",\"x\":\"101\"}," + + "\"tBox\":{\"adaptedT\":{\"x\":[\"111\"]},\"crateStrField\":\"T_BOX_STR\"}}", + marshalledJson); + + String toUnmarshall = "{\"intBox\":{\"crateStrField\":\"strCrateStr\",\"adaptedT\":{\"strField\":\"crateBoxStrField\",\"x\":[\"77\"]}}," + + "\"tBox\":{\"crateStrField\":\"tStrCrateStr\",\"adaptedT\":{\"strField\":\"crateBoxStrField\",\"x\":[\"88\"]}}," + + "\"strField\":\"POJO_STRING\"," + + "\"strBox\":{\"strField\":\"strBoxStr\",\"x\":\"44\"}}"; + + AdaptedPojo result = jsonb.fromJson(toUnmarshall, new TestTypeToken>() { + }.getType()); + assertEquals("POJO_STRING", result.strField); + assertEquals("strCrateStr", result.intBox.getStrField()); + assertEquals(77, result.intBox.getX()); + assertEquals("tStrCrateStr", result.tBox.getStrField()); + assertEquals(88, result.tBox.getX()); + assertEquals("strBoxStr", result.strBox.getStrField()); + assertEquals("44", result.strBox.getX()); + }); } @Test - public void testStringToGenericCollectionAdapter() throws Exception { + public void testStringToGenericCollectionAdapter() { JsonbAdapter[] adapters = {new IntegerListToStringAdapter()}; - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters)); - - AdaptedPojo> pojo = new AdaptedPojo<>(); - pojo.tVar = Arrays.asList(11, 22, 33); - pojo.integerList = Arrays.asList(110, 111, 101); - String marshalledJson = jsonb.toJson(pojo, new TestTypeToken>>(){}.getType()); - assertEquals("{\"integerList\":\"110#111#101\"," + - "\"tVar\":\"11#22#33\"}", marshalledJson); - - String toUnmarshall = "{\"integerList\":\"11#22#33#44\",\"stringList\":[\"first\",\"second\"]," + - "\"tVar\":\"110#111#101\"}"; - - AdaptedPojo result = jsonb.fromJson(toUnmarshall, new TestTypeToken>>(){}.getType()); - List expectedIntegerList = Arrays.asList(11, 22, 33, 44); - List expectedStringList = Arrays.asList("first", "second"); - List expectedTList = Arrays.asList(110, 111, 101); - - assertEquals(expectedIntegerList, result.integerList); - assertEquals(expectedStringList, result.stringList); - assertEquals(expectedTList, result.tVar); + testWithJsonbBuilderCreate(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters), jsonb -> { + + AdaptedPojo> pojo = new AdaptedPojo<>(); + pojo.tVar = Arrays.asList(11, 22, 33); + pojo.integerList = Arrays.asList(110, 111, 101); + String marshalledJson = jsonb.toJson(pojo, new TestTypeToken>>() { + }.getType()); + assertEquals("{\"integerList\":\"110#111#101\"," + + "\"tVar\":\"11#22#33\"}", marshalledJson); + + String toUnmarshall = "{\"integerList\":\"11#22#33#44\",\"stringList\":[\"first\",\"second\"]," + + "\"tVar\":\"110#111#101\"}"; + + AdaptedPojo result = jsonb.fromJson(toUnmarshall, new TestTypeToken>>() { + }.getType()); + List expectedIntegerList = Arrays.asList(11, 22, 33, 44); + List expectedStringList = Arrays.asList("first", "second"); + List expectedTList = Arrays.asList(110, 111, 101); + + assertEquals(expectedIntegerList, result.integerList); + assertEquals(expectedStringList, result.stringList); + assertEquals(expectedTList, result.tVar); + }); } @Test - public void testAdaptObjectInCollection() throws Exception { + public void testAdaptObjectInCollection() { JsonbAdapter[] adapters = {new BoxToCrateCompatibleGenericsAdapter() { }}; - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters)); - - AdaptedPojo pojo = new AdaptedPojo<>(); - - pojo.tGenericBoxList = new ArrayList<>(); - pojo.tGenericBoxList.add(new GenericBox<>("GEN_BOX_STR_1", 110)); - pojo.tGenericBoxList.add(new GenericBox<>("GEN_BOX_STR_2", 101)); - - String marshalledJson = jsonb.toJson(pojo, new TestTypeToken>(){}.getType()); - assertEquals("{\"tGenericBoxList\":[{\"adaptedT\":110,\"crateStrField\":\"GEN_BOX_STR_1\"},{\"adaptedT\":101,\"crateStrField\":\"GEN_BOX_STR_2\"}]}", marshalledJson); - - String toUnmarshall = "{\"integerList\":[11,22,33,44],\"stringList\":[\"first\",\"second\"]," + - "\"tGenericBoxList\":[{\"crateStrField\":\"FirstCrate\",\"adaptedT\":11},{\"crateStrField\":\"SecondCrate\",\"adaptedT\":22}]}"; - - AdaptedPojo result = jsonb.fromJson(toUnmarshall, new TestTypeToken>(){}.getType()); - assertEquals("FirstCrate", result.tGenericBoxList.get(0).getStrField()); - assertEquals("SecondCrate", result.tGenericBoxList.get(1).getStrField()); - assertEquals(Integer.valueOf(11), result.tGenericBoxList.get(0).getX()); - assertEquals(Integer.valueOf(22), result.tGenericBoxList.get(1).getX()); + testWithJsonbBuilderCreate(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters), jsonb -> { + + AdaptedPojo pojo = new AdaptedPojo<>(); + + pojo.tGenericBoxList = new ArrayList<>(); + pojo.tGenericBoxList.add(new GenericBox<>("GEN_BOX_STR_1", 110)); + pojo.tGenericBoxList.add(new GenericBox<>("GEN_BOX_STR_2", 101)); + + String marshalledJson = jsonb.toJson(pojo, new TestTypeToken>() { + }.getType()); + assertEquals( + "{\"tGenericBoxList\":[{\"adaptedT\":110,\"crateStrField\":\"GEN_BOX_STR_1\"},{\"adaptedT\":101,\"crateStrField\":\"GEN_BOX_STR_2\"}]}", + marshalledJson); + + String toUnmarshall = "{\"integerList\":[11,22,33,44],\"stringList\":[\"first\",\"second\"]," + + "\"tGenericBoxList\":[{\"crateStrField\":\"FirstCrate\",\"adaptedT\":11},{\"crateStrField\":\"SecondCrate\",\"adaptedT\":22}]}"; + + AdaptedPojo result = jsonb.fromJson(toUnmarshall, new TestTypeToken>() { + }.getType()); + assertEquals("FirstCrate", result.tGenericBoxList.get(0).getStrField()); + assertEquals("SecondCrate", result.tGenericBoxList.get(1).getStrField()); + assertEquals(Integer.valueOf(11), result.tGenericBoxList.get(0).getX()); + assertEquals(Integer.valueOf(22), result.tGenericBoxList.get(1).getX()); + }); } @Test - public void testAdaptTypeIntoCollection() throws Exception { + public void testAdaptTypeIntoCollection() { JsonbAdapter[] adapters = {new JsonbAdapter>() { @Override @@ -251,34 +281,40 @@ public String adaptFromJson(List ints) { } } }; - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters)); - - String json = "{\"strValues\":[11,22,33]}"; - final NonGenericPojo object = new NonGenericPojo(); - object.strValues = "11,22,33"; - assertEquals(json, jsonb.toJson(object)); - NonGenericPojo pojo = jsonb.fromJson(json, NonGenericPojo.class); - assertEquals("11,22,33", pojo.strValues); + testWithJsonbBuilderCreate(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters), jsonb -> { + + String json = "{\"strValues\":[11,22,33]}"; + final NonGenericPojo object = new NonGenericPojo(); + object.strValues = "11,22,33"; + assertEquals(json, jsonb.toJson(object)); + NonGenericPojo pojo = jsonb.fromJson(json, NonGenericPojo.class); + assertEquals("11,22,33", pojo.strValues); + }); } @Test - public void testMarshallGenericField() throws Exception { + public void testMarshallGenericField() { JsonbAdapter[] adapters = {new BoxToCratePropagatedIntegerStringAdapter()}; - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters)); - - AdaptedPojo adaptedPojo = new AdaptedPojo<>(); - adaptedPojo.tBox = new GenericBox<>("tGenBoxStrField", 22); - adaptedPojo.intBox = new GenericBox<>("genBoxStrField", 11); - String json = jsonb.toJson(adaptedPojo, new TestTypeToken>(){}.getType()); - assertEquals("{\"intBox\":{\"adaptedT\":{\"x\":[\"11\"]},\"crateStrField\":\"genBoxStrField\"},\"tBox\":{\"adaptedT\":{\"x\":[\"22\"]},\"crateStrField\":\"tGenBoxStrField\"}}", json); - - AdaptedPojo unmarshalledAdaptedPojo = jsonb.fromJson(json, new TestTypeToken>(){}.getType()); - assertEquals("genBoxStrField", unmarshalledAdaptedPojo.intBox.getStrField()); - assertEquals(Integer.valueOf(11), unmarshalledAdaptedPojo.intBox.getX()); + testWithJsonbBuilderCreate(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters), jsonb -> { + + AdaptedPojo adaptedPojo = new AdaptedPojo<>(); + adaptedPojo.tBox = new GenericBox<>("tGenBoxStrField", 22); + adaptedPojo.intBox = new GenericBox<>("genBoxStrField", 11); + String json = jsonb.toJson(adaptedPojo, new TestTypeToken>() { + }.getType()); + assertEquals( + "{\"intBox\":{\"adaptedT\":{\"x\":[\"11\"]},\"crateStrField\":\"genBoxStrField\"},\"tBox\":{\"adaptedT\":{\"x\":[\"22\"]},\"crateStrField\":\"tGenBoxStrField\"}}", + json); + + AdaptedPojo unmarshalledAdaptedPojo = jsonb.fromJson(json, new TestTypeToken>() { + }.getType()); + assertEquals("genBoxStrField", unmarshalledAdaptedPojo.intBox.getStrField()); + assertEquals(Integer.valueOf(11), unmarshalledAdaptedPojo.intBox.getX()); + }); } @Test - public void testTypeVariable() throws Exception { + public void testTypeVariable() { JsonbAdapter[] adapters = {new JsonbAdapter>, BigDecimal>() { @Override public BigDecimal adaptToJson(List> genericBoxes) { @@ -292,22 +328,25 @@ public List> adaptFromJson(BigDecimal bigDecimal) { return list; } }}; - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters)); + testWithJsonbBuilderCreate(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters), jsonb -> { - AdaptedPojo>> intBoxPojo = new AdaptedPojo<>(); - List> intBoxList = new ArrayList<>(); - intBoxList.add(new GenericBox<>("", 11d)); - intBoxPojo.tVar = intBoxList; + AdaptedPojo>> intBoxPojo = new AdaptedPojo<>(); + List> intBoxList = new ArrayList<>(); + intBoxList.add(new GenericBox<>("", 11d)); + intBoxPojo.tVar = intBoxList; - String json = jsonb.toJson(intBoxPojo, new TestTypeToken>>>(){}.getType()); - assertEquals("{\"tVar\":11.0}", json); + String json = jsonb.toJson(intBoxPojo, new TestTypeToken>>>() { + }.getType()); + assertEquals("{\"tVar\":11.0}", json); - AdaptedPojo>> result = jsonb.fromJson(json, new TestTypeToken>>>(){}.getType()); - assertEquals(Double.valueOf(11), result.tVar.get(0).getX()); + AdaptedPojo>> result = jsonb.fromJson(json, new TestTypeToken>>>() { + }.getType()); + assertEquals(Double.valueOf(11), result.tVar.get(0).getX()); + }); } @Test - public void testAdaptRoot() throws Exception { + public void testAdaptRoot() { JsonbAdapter[] adapters = {new JsonbAdapter() { @Override @@ -320,33 +359,34 @@ public Box adaptFromJson(Crate crate) { return new Box(crate.getCrateStrField(), crate.getCrateIntField()); } }}; - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters)); + testWithJsonbBuilderCreate(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters), jsonb -> { - Box pojo = new Box("BOX_STR", 101); - String marshalledJson = jsonb.toJson(pojo); - assertEquals("{\"crateIntField\":101,\"crateStrField\":\"BOX_STR\"}", marshalledJson); + Box pojo = new Box("BOX_STR", 101); + String marshalledJson = jsonb.toJson(pojo); + assertEquals("{\"crateIntField\":101,\"crateStrField\":\"BOX_STR\"}", marshalledJson); - Box result = jsonb.fromJson("{\"crateIntField\":110,\"crateStrField\":\"CRATE_STR\"}", Box.class); - assertEquals("CRATE_STR", result.getBoxStrField()); - assertEquals(Integer.valueOf(110), result.getBoxIntegerField()); + Box result = jsonb.fromJson("{\"crateIntField\":110,\"crateStrField\":\"CRATE_STR\"}", Box.class); + assertEquals("CRATE_STR", result.getBoxStrField()); + assertEquals(Integer.valueOf(110), result.getBoxIntegerField()); + }); } @Test - public void testAdaptMapString() throws Exception { + public void testAdaptMapString() { JsonbAdapter[] adapters = {new JsonbAdapter, String>() { @Override - public Map adaptFromJson(String obj) throws Exception { + public Map adaptFromJson(String obj) { final HashMap result = new HashMap<>(); result.put("fake", 101); return result; } @Override - public String adaptToJson(Map obj) throws Exception { + public String adaptToJson(Map obj) { StringBuilder sb = new StringBuilder(); for (Map.Entry entry : obj.entrySet()) { - if (sb.length() > 0) { + if (!sb.toString().isEmpty()) { sb.append("#"); } sb.append(entry.getKey()).append("-").append(entry.getValue()); @@ -354,50 +394,56 @@ public String adaptToJson(Map obj) throws Exception { return sb.toString(); } }}; - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters)); - - AdaptedPojo pojo = new AdaptedPojo<>(); - pojo.stringIntegerMap = new HashMap<>(); - pojo.stringIntegerMap.put("first", 11); - pojo.stringIntegerMap.put("second", 22); - pojo.tMap = new HashMap<>(pojo.stringIntegerMap); - String marshalledJson = jsonb.toJson(pojo, new AdaptedPojo(){}.getClass()); - assertEquals("{\"stringIntegerMap\":\"first-11#second-22\",\"tMap\":\"first-11#second-22\"}", marshalledJson); - - AdaptedPojo result = jsonb.fromJson("{\"stringIntegerMap\":\"fake-value\",\"tMap\":\"fake-value\"}", new TestTypeToken>(){}.getType()); - assertEquals(Integer.valueOf(101), result.stringIntegerMap.get("fake")); - assertEquals(Integer.valueOf(101), result.tMap.get("fake")); + testWithJsonbBuilderCreate(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters), jsonb -> { + + AdaptedPojo pojo = new AdaptedPojo<>(); + pojo.stringIntegerMap = new HashMap<>(); + pojo.stringIntegerMap.put("first", 11); + pojo.stringIntegerMap.put("second", 22); + pojo.tMap = new HashMap<>(pojo.stringIntegerMap); + String marshalledJson = jsonb.toJson(pojo, new AdaptedPojo() { + }.getClass()); + assertEquals("{\"stringIntegerMap\":\"first-11#second-22\",\"tMap\":\"first-11#second-22\"}", marshalledJson); + + AdaptedPojo result = + jsonb.fromJson("{\"stringIntegerMap\":\"fake-value\",\"tMap\":\"fake-value\"}", new TestTypeToken>() { + }.getType()); + assertEquals(Integer.valueOf(101), result.stringIntegerMap.get("fake")); + assertEquals(Integer.valueOf(101), result.tMap.get("fake")); + }); } @Test - public void testAdaptMapToObject() throws Exception { + public void testAdaptMapToObject() { JsonbAdapter[] adapters = {new JsonbAdapter, Crate>() { @Override - public Map adaptFromJson(Crate obj) throws Exception { + public Map adaptFromJson(Crate obj) { final HashMap fake = new HashMap<>(); fake.put("fake", "11"); return fake; } @Override - public Crate adaptToJson(Map obj) throws Exception { + public Crate adaptToJson(Map obj) { final Map.Entry next = obj.entrySet().iterator().next(); return new Crate(next.getKey(), Integer.parseInt(next.getValue())); } }}; - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters)); + testWithJsonbBuilderCreate(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters), jsonb -> { - AdaptedPojo pojo = new AdaptedPojo<>(); - pojo.tMap = new HashMap<>(); - pojo.tMap.put("first", "101"); + AdaptedPojo pojo = new AdaptedPojo<>(); + pojo.tMap = new HashMap<>(); + pojo.tMap.put("first", "101"); - TestTypeToken> typeToken = new TestTypeToken>() {}; + TestTypeToken> typeToken = new TestTypeToken<>() { + }; - String marshalledJson = jsonb.toJson(pojo, typeToken.getType()); - assertEquals("{\"tMap\":{\"crateIntField\":101,\"crateStrField\":\"first\"}}", marshalledJson); + String marshalledJson = jsonb.toJson(pojo, typeToken.getType()); + assertEquals("{\"tMap\":{\"crateIntField\":101,\"crateStrField\":\"first\"}}", marshalledJson); - AdaptedPojo result = jsonb.fromJson("{\"tMap\":{\"crateIntField\":101,\"crateStrField\":\"first\"}}", typeToken.getType()); - assertEquals("11", result.tMap.get("fake")); + AdaptedPojo result = jsonb.fromJson("{\"tMap\":{\"crateIntField\":101,\"crateStrField\":\"first\"}}", typeToken.getType()); + assertEquals("11", result.tMap.get("fake")); + }); } @Test @@ -429,18 +475,19 @@ public void testAdaptAuthor() { @Test public void testAdapterReturningNull() { - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withAdapters(new ReturnNullAdapter()).withNullValues(true)); + testWithJsonbBuilderCreate(new JsonbConfig().withAdapters(new ReturnNullAdapter()).withNullValues(true), jsonb -> { - ScalarValueWrapper wrapper = new ScalarValueWrapper<>(); - wrapper.setValue(10); - Type type = new TestTypeToken>() { - }.getType(); - String json = jsonb.toJson(wrapper, type); + ScalarValueWrapper wrapper = new ScalarValueWrapper<>(); + wrapper.setValue(10); + Type type = new TestTypeToken>() { + }.getType(); + String json = jsonb.toJson(wrapper, type); - assertEquals("{\"value\":null}", json); + assertEquals("{\"value\":null}", json); - ScalarValueWrapper result = jsonb.fromJson("{\"value\":null}", type); - assertNull(result.getValue()); + ScalarValueWrapper result = jsonb.fromJson("{\"value\":null}", type); + assertNull(result.getValue()); + }); } @Test @@ -460,6 +507,7 @@ public void testAdaptUUID() { @Test public void testSupertypeAdapter() { + NumberAdapter.getCounter().resetCount(); SupertypeAdapterPojo pojo = new SupertypeAdapterPojo(); pojo.setNumberInteger(10); pojo.setSerializableInteger(11); @@ -467,8 +515,26 @@ public void testSupertypeAdapter() { pojo = defaultJsonb.fromJson("{\"numberInteger\":\"11\",\"serializableInteger\":12}", SupertypeAdapterPojo.class); assertEquals(Integer.valueOf(10), pojo.getNumberInteger()); assertEquals(Integer.valueOf(11), pojo.getSerializableInteger()); + //assert that the adapter was used just once + assertEquals(1, NumberAdapter.getCounter().getCount()); } + @Test + public void testSupertypeAdapter_withConfiguration() { + NumberAdapter.getCounter().resetCount(); + testWithJsonbBuilderCreate(new JsonbConfig().withAdapters(new NumberAdapter()), jsonb -> { + SupertypeAdapterPojo pojo = new SupertypeAdapterPojo(); + pojo.setNumberInteger(10); + pojo.setSerializableInteger(11); + assertEquals("{\"numberInteger\":\"11\",\"serializableInteger\":12}", jsonb.toJson(pojo)); + pojo = jsonb.fromJson("{\"numberInteger\":\"11\",\"serializableInteger\":12}", SupertypeAdapterPojo.class); + assertEquals(Integer.valueOf(10), pojo.getNumberInteger()); + assertEquals(Integer.valueOf(11), pojo.getSerializableInteger()); + //assert that the adapter was reused + assertEquals(1, NumberAdapter.getCounter().getCount()); + }); + } + public static class PropertyTypeMismatch { private Throwable error = new RuntimeException("foo"); @@ -509,12 +575,13 @@ public Throwable adaptFromJson(Map obj) throws Exception { @Test public void testOptionalAdapter() { ThrowableAdapter adapter = new ThrowableAdapter(); - Jsonb jsonb = JsonbBuilder.newBuilder().withConfig(new JsonbConfig().withAdapters(adapter)).build(); - - PropertyTypeMismatch obj = new PropertyTypeMismatch(); - String json = jsonb.toJson(obj); - assertEquals("{\"error\":{\"message\":\"foo\",\"type\":\"java.lang.RuntimeException\"}}", json); - assertEquals(1, adapter.callCount, "The user-defined ThrowableAdapter should have been called"); + testWithJsonbBuilderNewBuilder(new JsonbConfig().withAdapters(adapter), jsonb -> { + + PropertyTypeMismatch obj = new PropertyTypeMismatch(); + String json = jsonb.toJson(obj); + assertEquals("{\"error\":{\"message\":\"foo\",\"type\":\"java.lang.RuntimeException\"}}", json); + assertEquals(1, adapter.callCount, "The user-defined ThrowableAdapter should have been called"); + }); } public static class InstantAdapter implements JsonbAdapter { @@ -543,19 +610,18 @@ public Instant adaptFromJson(String obj) throws Exception { public void testDifferentAdapters() { ThrowableAdapter throwableAdapter = new ThrowableAdapter(); InstantAdapter instantAdapter = new InstantAdapter(); - Jsonb jsonb = JsonbBuilder.newBuilder() - .withConfig(new JsonbConfig().withAdapters(throwableAdapter, instantAdapter)) - .build(); - - String json = "{\"error\":\"CUSTOM_VALUE\"}"; - PropertyTypeMismatch obj = jsonb.fromJson(json, PropertyTypeMismatch.class); - assertEquals("Error at: +1000000000-12-31T23:59:59.999999999Z", obj.getError().get().getMessage()); - assertEquals(1, instantAdapter.callCount); - - String afterJson = jsonb.toJson(obj); - assertEquals("{\"error\":{\"message\":\"Error at: +1000000000-12-31T23:59:59.999999999Z\",\"type\":\"java.lang.RuntimeException\"}}", - afterJson); - assertEquals(1, throwableAdapter.callCount); + testWithJsonbBuilderNewBuilder(new JsonbConfig().withAdapters(throwableAdapter, instantAdapter), jsonb -> { + + String json = "{\"error\":\"CUSTOM_VALUE\"}"; + PropertyTypeMismatch obj = jsonb.fromJson(json, PropertyTypeMismatch.class); + assertEquals("Error at: +1000000000-12-31T23:59:59.999999999Z", obj.getError().orElseThrow().getMessage()); + assertEquals(1, instantAdapter.callCount); + + String afterJson = jsonb.toJson(obj); + assertEquals("{\"error\":{\"message\":\"Error at: +1000000000-12-31T23:59:59.999999999Z\",\"type\":\"java.lang.RuntimeException\"}}", + afterJson); + assertEquals(1, throwableAdapter.callCount); + }); } public static class StringAdapter implements JsonbAdapter { @@ -571,16 +637,43 @@ public String adaptFromJson(String obj) throws Exception { } /** - * Test for: https://github.com/eclipse-ee4j/yasson/issues/346 + * Test for: issue 346 */ @Test public void testAdaptedRootType() { - Jsonb jsonb = JsonbBuilder.newBuilder() - .withConfig(new JsonbConfig().withAdapters(new StringAdapter())) - .build(); - - String original = "hello world!"; - assertEquals("\"HELLO WORLD!\"", jsonb.toJson(original)); - assertEquals(original, jsonb.fromJson("\"HELLO WORLD!\"", String.class)); + testWithJsonbBuilderNewBuilder(new JsonbConfig().withAdapters(new StringAdapter()), jsonb -> { + String original = "hello world!"; + assertEquals("\"HELLO WORLD!\"", jsonb.toJson(original)); + assertEquals(original, jsonb.fromJson("\"HELLO WORLD!\"", String.class)); + }); + } + + @Test + public void testCustomAdapterInEnum() { + Vegetables expected = Vegetables.TOMATO; + + String expectedJson = defaultJsonb.toJson(expected); + + assertEquals(expected, defaultJsonb.fromJson(expectedJson, Vegetables.class)); + } + + @Test + void testTypeToSameTypeAdapter_shouldntBeStackOverflowError() { + RuntimeException runtimeException + = assertThrows(RuntimeException.class, () -> testWithJsonbBuilderNewBuilder(new JsonbConfig().withAdapters( + new JsonbAdapter() { + @Override + public Box adaptToJson(Box obj) { + return obj; + } + + @Override + public Box adaptFromJson(Box obj) { + return obj; + } + }), + jsonb -> jsonb.toJson(new Box()))); + + assertInstanceOf(JsonbException.class, runtimeException.getCause()); } } diff --git a/src/test/java/org/eclipse/yasson/adapters/JsonbTypeAdapterTest.java b/src/test/java/org/eclipse/yasson/adapters/JsonbTypeAdapterTest.java index df1ecbc56..b12e9eb30 100644 --- a/src/test/java/org/eclipse/yasson/adapters/JsonbTypeAdapterTest.java +++ b/src/test/java/org/eclipse/yasson/adapters/JsonbTypeAdapterTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024 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 @@ -60,8 +60,8 @@ public static class AnnotatedPojo { } @Test - public void testIncompatibleAdapter() throws Exception { - IncompatibleAdapterPojo incompatibleAdapterFieldPojo = new IncompatibleAdapterPojo(); + public void testIncompatibleAdapter() { + IncompatibleAdapterPojo incompatibleAdapterFieldPojo = new IncompatibleAdapterPojo<>(); incompatibleAdapterFieldPojo.str = "STR"; try { defaultJsonb.toJson(incompatibleAdapterFieldPojo); @@ -85,8 +85,8 @@ public void testGenericFieldsMatch() { } @Test - public void testAnnotatedTbox() throws Exception { - AnnotatedPojo pojo = new AnnotatedPojo(); + public void testAnnotatedTbox() { + AnnotatedPojo pojo = new AnnotatedPojo<>(); pojo.box = new Box("STR", 101); String marshalledJson = defaultJsonb.toJson(pojo); assertEquals("{\"box\":\"STR:101\"}", marshalledJson); diff --git a/src/test/java/org/eclipse/yasson/adapters/model/EnumJsonbPropertyMaps.java b/src/test/java/org/eclipse/yasson/adapters/model/EnumJsonbPropertyMaps.java new file mode 100644 index 000000000..353f8f75a --- /dev/null +++ b/src/test/java/org/eclipse/yasson/adapters/model/EnumJsonbPropertyMaps.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2024 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.adapters.model; + +import static java.util.Optional.ofNullable; + +import java.util.EnumMap; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; + +import jakarta.json.bind.annotation.JsonbProperty; + +public class EnumJsonbPropertyMaps> { + private final Map jsonToJavaMapping = new HashMap<>(); + private final EnumMap javaToJsonMapping; + + public EnumJsonbPropertyMaps(Class enumType) { + super(); + + javaToJsonMapping = new EnumMap<>(enumType); + + Stream.of(enumType.getEnumConstants()).forEach(constant -> { + final String asString; + try { + asString = ofNullable( + constant.getClass() + .getDeclaredField(constant.name()) + .getAnnotation(JsonbProperty.class)) + .map(JsonbProperty::value) + .orElseGet(constant::name); + } catch (final NoSuchFieldException e) { + throw new IllegalArgumentException(e); + } + javaToJsonMapping.put(constant, asString); + jsonToJavaMapping.put(asString, constant); + }); + } + + public EnumMap getJavaToJsonMapping() { + return javaToJsonMapping; + } + + public Map getJsonToJavaMapping() { + return jsonToJavaMapping; + } +} diff --git a/src/test/java/org/eclipse/yasson/adapters/model/EnumWithJsonbPropertyAdapter.java b/src/test/java/org/eclipse/yasson/adapters/model/EnumWithJsonbPropertyAdapter.java new file mode 100644 index 000000000..feb802093 --- /dev/null +++ b/src/test/java/org/eclipse/yasson/adapters/model/EnumWithJsonbPropertyAdapter.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2024 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.adapters.model; + +import static java.util.Optional.ofNullable; + +import java.lang.reflect.ParameterizedType; +import java.util.EnumMap; +import java.util.Map; + +import jakarta.json.bind.adapter.JsonbAdapter; + +public class EnumWithJsonbPropertyAdapter> implements JsonbAdapter { + private final Map jsonToJavaMapping; + private final EnumMap javaToJsonMapping; + + public EnumWithJsonbPropertyAdapter() { + super(); + + EnumJsonbPropertyMaps enumMappingMaps = new EnumJsonbPropertyMaps<>(getEnumType()); + jsonToJavaMapping = enumMappingMaps.getJsonToJavaMapping(); + javaToJsonMapping = enumMappingMaps.getJavaToJsonMapping(); + } + + private Class getEnumType() { + @SuppressWarnings("unchecked") + Class cast = Class.class.cast(((ParameterizedType) getClass().getGenericSuperclass()) + .getActualTypeArguments()[0]); + return cast; + } + + @Override + public String adaptToJson(final E obj) { + return javaToJsonMapping.get(obj); + } + + @Override + public E adaptFromJson(final String obj) { + return ofNullable(jsonToJavaMapping.get(obj)) + .orElseThrow(() -> new IllegalArgumentException("Unknown enum value: '" + obj + "'")); + } +} diff --git a/src/test/java/org/eclipse/yasson/adapters/model/LocalPolymorphicAdapter.java b/src/test/java/org/eclipse/yasson/adapters/model/LocalPolymorphicAdapter.java index c77530b0d..3d76cfe85 100644 --- a/src/test/java/org/eclipse/yasson/adapters/model/LocalPolymorphicAdapter.java +++ b/src/test/java/org/eclipse/yasson/adapters/model/LocalPolymorphicAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024 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 @@ -28,7 +28,7 @@ public abstract class LocalPolymorphicAdapter implements JsonbAdapter... allowedClasses) { this.allowedClasses = Stream.of(allowedClasses).map(Class::getName).toArray(value -> new String[allowedClasses.length]); } diff --git a/src/test/java/org/eclipse/yasson/adapters/model/MultilevelAdapterClass.java b/src/test/java/org/eclipse/yasson/adapters/model/MultilevelAdapterClass.java index 70a6211f9..c6c4d027e 100644 --- a/src/test/java/org/eclipse/yasson/adapters/model/MultilevelAdapterClass.java +++ b/src/test/java/org/eclipse/yasson/adapters/model/MultilevelAdapterClass.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024 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 @@ -13,4 +13,5 @@ package org.eclipse.yasson.adapters.model; public abstract class MultilevelAdapterClass implements MultiinterfaceAdapter { + private static final long serialVersionUID = 1L; } diff --git a/src/test/java/org/eclipse/yasson/adapters/model/NumberAdapter.java b/src/test/java/org/eclipse/yasson/adapters/model/NumberAdapter.java index 2439807d2..27333689a 100644 --- a/src/test/java/org/eclipse/yasson/adapters/model/NumberAdapter.java +++ b/src/test/java/org/eclipse/yasson/adapters/model/NumberAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024 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 @@ -12,9 +12,22 @@ package org.eclipse.yasson.adapters.model; +import org.eclipse.yasson.serializers.model.Counter; + import jakarta.json.bind.adapter.JsonbAdapter; public class NumberAdapter implements JsonbAdapter { + + private final static Counter COUNTER = new Counter(); + + public static Counter getCounter() { + return COUNTER; + } + + { + COUNTER.add(); + } + @Override public String adaptToJson(Number obj) throws Exception { return Integer.valueOf(((Integer)obj) + 1).toString(); diff --git a/src/test/java/org/eclipse/yasson/adapters/model/UUIDMapperClsBased.java b/src/test/java/org/eclipse/yasson/adapters/model/UUIDMapperClsBased.java index cc7a4e5a0..b3dd71503 100644 --- a/src/test/java/org/eclipse/yasson/adapters/model/UUIDMapperClsBased.java +++ b/src/test/java/org/eclipse/yasson/adapters/model/UUIDMapperClsBased.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024 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 @@ -17,6 +17,8 @@ public class UUIDMapperClsBased extends MultilevelAdapterClass { + private static final long serialVersionUID = 1L; + @Override public String adaptToJson(UUID obj) throws Exception { return Optional.ofNullable(obj).map(UUID::toString).orElse(null); diff --git a/src/test/java/org/eclipse/yasson/adapters/model/UUIDMapperIfcBased.java b/src/test/java/org/eclipse/yasson/adapters/model/UUIDMapperIfcBased.java index 62e99d8c8..2eefb85dc 100644 --- a/src/test/java/org/eclipse/yasson/adapters/model/UUIDMapperIfcBased.java +++ b/src/test/java/org/eclipse/yasson/adapters/model/UUIDMapperIfcBased.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024 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 @@ -17,6 +17,8 @@ public class UUIDMapperIfcBased implements MultiinterfaceAdapter { + private static final long serialVersionUID = 1L; + @Override public String adaptToJson(UUID obj) throws Exception { return Optional.ofNullable(obj).map(UUID::toString).orElse(null); diff --git a/src/test/java/org/eclipse/yasson/adapters/model/Vegetables.java b/src/test/java/org/eclipse/yasson/adapters/model/Vegetables.java new file mode 100644 index 000000000..bcf40b358 --- /dev/null +++ b/src/test/java/org/eclipse/yasson/adapters/model/Vegetables.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2024 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.adapters.model; + +import jakarta.json.bind.annotation.JsonbProperty; +import jakarta.json.bind.annotation.JsonbTypeAdapter; + +@JsonbTypeAdapter(VegetablesAdapter.class) +public enum Vegetables { + @JsonbProperty("Tomato") + TOMATO, + @JsonbProperty("Cucumber") + CUCUMBER +} + diff --git a/src/test/java/org/eclipse/yasson/adapters/model/VegetablesAdapter.java b/src/test/java/org/eclipse/yasson/adapters/model/VegetablesAdapter.java new file mode 100644 index 000000000..dde495199 --- /dev/null +++ b/src/test/java/org/eclipse/yasson/adapters/model/VegetablesAdapter.java @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2024 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.adapters.model; + +public class VegetablesAdapter extends EnumWithJsonbPropertyAdapter { + public VegetablesAdapter() { + super(); + } +} diff --git a/src/test/java/org/eclipse/yasson/customization/EncodingTest.java b/src/test/java/org/eclipse/yasson/customization/EncodingTest.java index a47bf636b..d44c47622 100644 --- a/src/test/java/org/eclipse/yasson/customization/EncodingTest.java +++ b/src/test/java/org/eclipse/yasson/customization/EncodingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024 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 @@ -13,12 +13,13 @@ package org.eclipse.yasson.customization; import org.junit.jupiter.api.*; + +import static org.eclipse.yasson.Jsonbs.testWithJsonbBuilderCreate; import static org.junit.jupiter.api.Assertions.*; import org.eclipse.yasson.TestTypeToken; import jakarta.json.bind.Jsonb; -import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -50,32 +51,47 @@ public class EncodingTest { } @Test - public void testCP1250Encoding() throws UnsupportedEncodingException { + public void testCP1250Encoding() { String encoding = "cp1250"; - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withEncoding(encoding)); - - testMarshaller(CZECH, jsonb, encoding); - testUnmarshaller(CZECH, jsonb, encoding); + testWithJsonbBuilderCreate(new JsonbConfig().withEncoding(encoding), jsonb -> { + + try { + testMarshaller(CZECH, jsonb, encoding); + testUnmarshaller(CZECH, jsonb, encoding); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + }); } @Test - public void testUTF8Encoding() throws UnsupportedEncodingException { + public void testUTF8Encoding() { String encoding = "UTF-8"; - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withEncoding(encoding)); - - testMarshaller(CZECH, jsonb, encoding); - testUnmarshaller(CZECH, jsonb, encoding); - testMarshaller(RUSSIAN, jsonb, encoding); - testUnmarshaller(RUSSIAN, jsonb, encoding); + testWithJsonbBuilderCreate(new JsonbConfig().withEncoding(encoding), jsonb -> { + + try { + testMarshaller(CZECH, jsonb, encoding); + testUnmarshaller(CZECH, jsonb, encoding); + testMarshaller(RUSSIAN, jsonb, encoding); + testUnmarshaller(RUSSIAN, jsonb, encoding); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + }); } @Test - public void testcp1251Encoding() throws UnsupportedEncodingException { + public void testcp1251Encoding() { String encoding = "cp1251"; - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withEncoding(encoding)); - - testMarshaller(RUSSIAN, jsonb, encoding); - testUnmarshaller(RUSSIAN, jsonb, encoding); + testWithJsonbBuilderCreate(new JsonbConfig().withEncoding(encoding), jsonb -> { + + try { + testMarshaller(RUSSIAN, jsonb, encoding); + testUnmarshaller(RUSSIAN, jsonb, encoding); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + }); } private static void testMarshaller(String[] input, Jsonb jsonb, String encoding) throws UnsupportedEncodingException { @@ -92,7 +108,7 @@ private static void testUnmarshaller(String[] input, Jsonb jsonb, String encodin logger.finest("JSON for unmarshaller: "+json); InputStream bis = new ByteArrayInputStream(json.getBytes(encoding)); ArrayList result = jsonb.fromJson(bis, new TestTypeToken>(){}.getType()); - assertArrayEquals(input, result.toArray(new String[result.size()])); + assertArrayEquals(input, result.toArray(new String[0])); } private static String diacriticsToJsonArray(String[] diacritics) { diff --git a/src/test/java/org/eclipse/yasson/customization/ImplementationClassTest.java b/src/test/java/org/eclipse/yasson/customization/ImplementationClassTest.java index 51f0e7cae..6f03dee7c 100644 --- a/src/test/java/org/eclipse/yasson/customization/ImplementationClassTest.java +++ b/src/test/java/org/eclipse/yasson/customization/ImplementationClassTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2024 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 @@ -20,8 +20,6 @@ import org.eclipse.yasson.customization.model.Dog; import org.eclipse.yasson.customization.model.ImplementationClassPojo; -import jakarta.json.bind.Jsonb; -import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import java.util.HashMap; @@ -39,22 +37,23 @@ public void testAnnotatedImplementation() { assertEquals(expected, json); ImplementationClassPojo result = defaultJsonb.fromJson(expected, ImplementationClassPojo.class); - assertTrue(result.getAnimal() instanceof Dog); + assertInstanceOf(Dog.class, result.getAnimal()); assertEquals("Bulldog", ((Dog)result.getAnimal()).getDogProperty()); } @Test public void testJsonbConfigUserImplementation() { - HashMap userMapping = new HashMap<>(); + HashMap, Class> userMapping = new HashMap<>(); userMapping.put(Animal.class, Dog.class); - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty(USER_TYPE_MAPPING, userMapping)); - Animal animal = new Dog("Bulldog"); - String expected = "{\"dogProperty\":\"Bulldog\"}"; - String json = jsonb.toJson(animal); + testWithJsonbBuilderCreate(new JsonbConfig().setProperty(USER_TYPE_MAPPING, userMapping), jsonb -> { + Animal animal = new Dog("Bulldog"); + String expected = "{\"dogProperty\":\"Bulldog\"}"; + String json = jsonb.toJson(animal); - assertEquals(expected, json); + assertEquals(expected, json); - Dog result = (Dog) jsonb.fromJson("{\"dogProperty\":\"Bulldog\"}", Animal.class); - assertEquals("Bulldog", result.getDogProperty()); + Dog result = (Dog) jsonb.fromJson("{\"dogProperty\":\"Bulldog\"}", Animal.class); + assertEquals("Bulldog", result.getDogProperty()); + }); } } diff --git a/src/test/java/org/eclipse/yasson/customization/JsonbCreatorTest.java b/src/test/java/org/eclipse/yasson/customization/JsonbCreatorTest.java index de4c44aea..b7300c235 100644 --- a/src/test/java/org/eclipse/yasson/customization/JsonbCreatorTest.java +++ b/src/test/java/org/eclipse/yasson/customization/JsonbCreatorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024 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 @@ -16,12 +16,14 @@ import java.time.LocalDate; import java.util.Set; +import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.JsonbException; import jakarta.json.bind.annotation.JsonbCreator; import jakarta.json.bind.annotation.JsonbDateFormat; import jakarta.json.bind.annotation.JsonbNumberFormat; import jakarta.json.bind.annotation.JsonbProperty; +import org.eclipse.yasson.YassonConfig; import org.eclipse.yasson.customization.model.CreatorConstructorPojo; import org.eclipse.yasson.customization.model.CreatorFactoryMethodPojo; import org.eclipse.yasson.customization.model.CreatorIncompatibleTypePojo; @@ -33,9 +35,11 @@ import org.junit.jupiter.api.Test; import static org.eclipse.yasson.Jsonbs.defaultJsonb; +import static org.eclipse.yasson.Jsonbs.testWithJsonbBuilderNewBuilder; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -156,13 +160,35 @@ public void testCorrectCreatorParameterNames() { } @Test - public void testGenericCreatorParameter() throws Exception { + public void testGenericCreatorParameter() { final String json = "{\"persons\": [{\"name\": \"name1\"}]}"; Persons persons = defaultJsonb.fromJson(json, Persons.class); assertEquals(1, persons.hiddenPersons.size()); assertEquals("name1", persons.hiddenPersons.iterator().next().getName()); } + @Test + public void testCreatorParametersRequired() { + RuntimeException runtimeException = assertThrows(RuntimeException.class, () -> + testWithJsonbBuilderNewBuilder(new JsonbConfig().withCreatorParametersRequired(true), jsonb -> { + final String json = "{\"persons1\": [{\"name\": \"name1\"}]}"; + jsonb.fromJson(json, Persons.class); + }) + ); + assertInstanceOf(JsonbException.class, runtimeException.getCause()); + } + + @Test + public void testCreatorFailOnUnknownProperties() { + RuntimeException runtimeException = assertThrows(RuntimeException.class, () -> + testWithJsonbBuilderNewBuilder(new YassonConfig().withFailOnUnknownProperties(true), jsonb -> { + final String json = "{\"persons1\": [{\"name\": \"name1\"}]}"; + jsonb.fromJson(json, Persons.class); + }) + ); + assertInstanceOf(JsonbException.class, runtimeException.getCause()); + } + public static final class Persons { Set hiddenPersons; diff --git a/src/test/java/org/eclipse/yasson/customization/JsonbNillableTest.java b/src/test/java/org/eclipse/yasson/customization/JsonbNillableTest.java index 1d5d40bf0..db4885f50 100644 --- a/src/test/java/org/eclipse/yasson/customization/JsonbNillableTest.java +++ b/src/test/java/org/eclipse/yasson/customization/JsonbNillableTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024 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 @@ -14,8 +14,6 @@ import org.junit.jupiter.api.*; -import jakarta.json.bind.Jsonb; -import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.annotation.JsonbProperty; import static org.junit.jupiter.api.Assertions.*; @@ -65,13 +63,13 @@ public void testPackageLevelOverriddenWithClassLevel() { * Tests inheritance of annotations from interfaces. */ @Test - public void testNillableInheritFromInterface() throws Exception { + public void testNillableInheritFromInterface() { JsonbNillableClassSecondLevel pojo = new JsonbNillableClassSecondLevel(); assertEquals("{\"classNillable\":null}", defaultJsonb.toJson(pojo)); } @Test - public void testInheritanceOverride() throws Exception { + public void testInheritanceOverride() { JsonbNillableOverridesInterface overridesInterface = new JsonbNillableOverridesInterface(); assertEquals("{}", defaultJsonb.toJson(overridesInterface)); @@ -87,6 +85,7 @@ public void testNillableInConfig() { public static class PrimitiveNullBoolean { + @SuppressWarnings("deprecation") @JsonbProperty(nillable = true) private Boolean someBoolean; @@ -96,13 +95,12 @@ void setSomeBoolean(boolean value) { // note that value is a primitive boolean } /** - * Test for issue https://github.com/eclipse-ee4j/yasson/issues/399 + * Test for issue 399 */ @Test public void testNillableSomeBoolean() { - Jsonb jsonb = JsonbBuilder.create(); String input = "{\"someBoolean\": null}"; - PrimitiveNullBoolean deserialized = jsonb.fromJson(input, PrimitiveNullBoolean.class); + PrimitiveNullBoolean deserialized = defaultJsonb.fromJson(input, PrimitiveNullBoolean.class); assertNull(deserialized.someBoolean); } } diff --git a/src/test/java/org/eclipse/yasson/customization/JsonbPropertyTest.java b/src/test/java/org/eclipse/yasson/customization/JsonbPropertyTest.java index 5e6c52aab..0521c9f94 100644 --- a/src/test/java/org/eclipse/yasson/customization/JsonbPropertyTest.java +++ b/src/test/java/org/eclipse/yasson/customization/JsonbPropertyTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024 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 @@ -20,8 +20,6 @@ import org.eclipse.yasson.customization.model.JsonbPropertyNameCollision; import org.eclipse.yasson.customization.model.JsonbPropertyNillable; -import jakarta.json.bind.Jsonb; -import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.JsonbException; import jakarta.json.bind.annotation.JsonbProperty; @@ -34,7 +32,7 @@ public class JsonbPropertyTest { @Test - public void testPropertyName() throws Exception { + public void testPropertyName() { JsonbPropertyName pojo = new JsonbPropertyName(); pojo.setFieldAnnotatedName("FIELD_ANNOTATED"); @@ -92,28 +90,29 @@ public void testRenamedGetterAndSetter() { @Test public void testRenamedGetterAndSetter2() { // Reported in issue: https://github.com/eclipse-ee4j/yasson/issues/81 - final Jsonb jsonb = JsonbBuilder.create( - new JsonbConfig().withPropertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE)); + testWithJsonbBuilderCreate(new JsonbConfig().withPropertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE), jsonb -> { - final RenamedGetterAndSetter2 bean1 = new RenamedGetterAndSetter2(); - bean1.setAPIDocumentation("REST"); + final RenamedGetterAndSetter2 bean1 = new RenamedGetterAndSetter2(); + bean1.setAPIDocumentation("REST"); - final String json = jsonb.toJson(bean1); - final RenamedGetterAndSetter2 bean2 = jsonb.fromJson(json, RenamedGetterAndSetter2.class); - assertEquals(bean1.getAPIDocumentation(), bean2.getAPIDocumentation()); + final String json = jsonb.toJson(bean1); + final RenamedGetterAndSetter2 bean2 = jsonb.fromJson(json, RenamedGetterAndSetter2.class); + assertEquals(bean1.getAPIDocumentation(), bean2.getAPIDocumentation()); + }); } @Test public void testRenamedGetterAndSetter3() { // Reported in issue: https://github.com/eclipse-ee4j/yasson/issues/81 - final Jsonb jsonb = JsonbBuilder.create(); - - final RenamedGetterAndSetter2 bean1 = new RenamedGetterAndSetter2(); - bean1.setAPIDocumentation("REST"); + testWithJsonbBuilderCreate(jsonb -> { + + final RenamedGetterAndSetter2 bean1 = new RenamedGetterAndSetter2(); + bean1.setAPIDocumentation("REST"); - final String json = jsonb.toJson(bean1); - final RenamedGetterAndSetter2 bean2 = jsonb.fromJson(json, RenamedGetterAndSetter2.class); - assertEquals(bean1.getAPIDocumentation(), bean2.getAPIDocumentation()); + final String json = jsonb.toJson(bean1); + final RenamedGetterAndSetter2 bean2 = jsonb.fromJson(json, RenamedGetterAndSetter2.class); + assertEquals(bean1.getAPIDocumentation(), bean2.getAPIDocumentation()); + }); } public static class RenamedGetterAndSetter { @@ -152,7 +151,7 @@ public void setAPIDocumentation(String api) { * is private without getter / setter. * And property "DOI" - getter / setter without a field with customization on getter renaming it to doi * in serialized document. - * + *

* Because first of those properties is not readable this should not raise naming clash error. */ @Test @@ -174,16 +173,18 @@ public void testNonConflictingProperties() { public void testConflictingProperties() { ConflictingProperties conflictingProperties = new ConflictingProperties(); conflictingProperties.setDOI("DOI value"); - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()); - - try { - jsonb.toJson(conflictingProperties); - fail(); - } catch (JsonbException e) { - if (!e.getMessage().equals("Property DOI clashes with property doi by read or write name in class org.eclipse.yasson.customization.JsonbPropertyTest$ConflictingProperties.")) { - throw e; + testWithJsonbBuilderCreate(new JsonbConfig(), jsonb -> { + + try { + jsonb.toJson(conflictingProperties); + fail(); + } catch (JsonbException e) { + if (!e.getMessage() + .equals("Property DOI clashes with property doi by read or write name in class org.eclipse.yasson.customization.JsonbPropertyTest$ConflictingProperties.")) { + throw e; + } } - } + }); } /** @@ -197,26 +198,26 @@ public void testConflictingWithUpperCamelStrategy() { String json = defaultJsonb.toJson(pojo); assertEquals("{\"Doi\":\"DOI value\",\"doi\":\"DOI value\"}", json); - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig() - .withPropertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE)); + testWithJsonbBuilderCreate(new JsonbConfig().withPropertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE), jsonb -> { - try { - jsonb.toJson(pojo); - fail(); - } catch (JsonbException e) { - if (!e.getMessage().equals("Property DOI clashes with property doi by read or write name in class org.eclipse.yasson.customization.JsonbPropertyTest$ConflictingWithUpperCamelStrategy.")) { - throw e; + try { + jsonb.toJson(pojo); + fail(); + } catch (JsonbException e) { + if (!e.getMessage() + .equals("Property DOI clashes with property doi by read or write name in class org.eclipse.yasson.customization.JsonbPropertyTest$ConflictingWithUpperCamelStrategy.")) { + throw e; + } } - } - + }); } @Test public void testConflictingWithLowercaseStrategy() { // scenario raised by user here: https://github.com/eclipse-ee4j/yasson/issues/296 - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CASE_WITH_UNDERSCORES)); - assertEquals("{\"url\":\"http://foo.com\"}", - jsonb.toJson(new ConflictingIfLowercase())); + testWithJsonbBuilderCreate(new JsonbConfig().withPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CASE_WITH_UNDERSCORES), jsonb -> + assertEquals("{\"url\":\"http://foo.com\"}", jsonb.toJson(new ConflictingIfLowercase())) + ); } public static class ConflictingIfLowercase { diff --git a/src/test/java/org/eclipse/yasson/customization/JsonbPropertyVisibilityStrategyTest.java b/src/test/java/org/eclipse/yasson/customization/JsonbPropertyVisibilityStrategyTest.java index 551e059ed..e4337c734 100644 --- a/src/test/java/org/eclipse/yasson/customization/JsonbPropertyVisibilityStrategyTest.java +++ b/src/test/java/org/eclipse/yasson/customization/JsonbPropertyVisibilityStrategyTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024 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 @@ -16,8 +16,6 @@ import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; -import jakarta.json.bind.Jsonb; -import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.annotation.JsonbVisibility; import jakarta.json.bind.config.PropertyVisibilityStrategy; @@ -110,24 +108,24 @@ public boolean isVisible(Method method) { */ @Test public void testFieldVisibilityStrategy() { - JsonbConfig customizedConfig = new JsonbConfig(); - customizedConfig.setProperty(JsonbConfig.PROPERTY_VISIBILITY_STRATEGY, new PropertyVisibilityStrategy() { - @Override - public boolean isVisible(Field field) { - final String fieldName = field.getName(); - return fieldName.equals("afield") || fieldName.equals("dfield"); - } - - @Override - public boolean isVisible(Method method) { - throw new IllegalStateException("Not supported"); - } - }); + JsonbConfig customizedConfig = new JsonbConfig() + .setProperty(JsonbConfig.PROPERTY_VISIBILITY_STRATEGY, new PropertyVisibilityStrategy() { + @Override + public boolean isVisible(Field field) { + final String fieldName = field.getName(); + return fieldName.equals("afield") || fieldName.equals("dfield"); + } + + @Override + public boolean isVisible(Method method) { + throw new IllegalStateException("Not supported"); + } + }); FieldPojo fieldPojo = new FieldPojo("avalue", "bvalue", "cvalue", "dvalue"); - Jsonb jsonb = JsonbBuilder.create(customizedConfig); - assertEquals("{\"afield\":\"avalue\",\"dfield\":\"dvalue\"}", jsonb.toJson(fieldPojo)); + testWithJsonbBuilderCreate(customizedConfig, jsonb -> + assertEquals("{\"afield\":\"avalue\",\"dfield\":\"dvalue\"}", jsonb.toJson(fieldPojo))); } /** @@ -135,24 +133,24 @@ public boolean isVisible(Method method) { */ @Test public void testMethodVisibilityStrategy() { - JsonbConfig customizedConfig = new JsonbConfig(); - customizedConfig.setProperty(JsonbConfig.PROPERTY_VISIBILITY_STRATEGY, new PropertyVisibilityStrategy() { - @Override - public boolean isVisible(Field field) { - throw new IllegalStateException("Not supported"); - } - - @Override - public boolean isVisible(Method method) { - final String methodName = method.getName(); - return methodName.equals("getAgetter") || methodName.equals("getDgetter"); - } - }); + JsonbConfig customizedConfig = new JsonbConfig() + .setProperty(JsonbConfig.PROPERTY_VISIBILITY_STRATEGY, new PropertyVisibilityStrategy() { + @Override + public boolean isVisible(Field field) { + throw new IllegalStateException("Not supported"); + } + + @Override + public boolean isVisible(Method method) { + final String methodName = method.getName(); + return methodName.equals("getAgetter") || methodName.equals("getDgetter"); + } + }); GetterPojo getterPojo = new GetterPojo(); - Jsonb jsonb = JsonbBuilder.create(customizedConfig); - assertEquals("{\"agetter\":\"avalue\",\"dgetter\":\"dvalue\"}", jsonb.toJson(getterPojo)); + testWithJsonbBuilderCreate(customizedConfig, jsonb -> + assertEquals("{\"agetter\":\"avalue\",\"dgetter\":\"dvalue\"}", jsonb.toJson(getterPojo))); } @Test diff --git a/src/test/java/org/eclipse/yasson/customization/PropertyOrderTest.java b/src/test/java/org/eclipse/yasson/customization/PropertyOrderTest.java index eee1d7dd3..406d9b63b 100644 --- a/src/test/java/org/eclipse/yasson/customization/PropertyOrderTest.java +++ b/src/test/java/org/eclipse/yasson/customization/PropertyOrderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024 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 @@ -23,8 +23,6 @@ import org.eclipse.yasson.customization.model.FieldSpecificOrder; import org.eclipse.yasson.customization.model.RenamedPropertiesContainer; -import jakarta.json.bind.Jsonb; -import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.annotation.JsonbCreator; import jakarta.json.bind.annotation.JsonbProperty; @@ -39,61 +37,72 @@ public class PropertyOrderTest { @Test public void testPropertySorting() { FieldOrder fieldOrder = new FieldOrder(); - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.LEXICOGRAPHICAL)); - String expectedLexicographical = "{\"aField\":\"aValue\",\"bField\":\"bValue\",\"cField\":\"cValue\",\"dField\":\"dValue\"}"; - assertEquals(expectedLexicographical, jsonb.toJson(fieldOrder)); - - jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.REVERSE)); - String expectedReverse = "{\"dField\":\"dValue\",\"cField\":\"cValue\",\"bField\":\"bValue\",\"aField\":\"aValue\"}"; - assertEquals(expectedReverse, jsonb.toJson(fieldOrder)); + testWithJsonbBuilderCreate(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.LEXICOGRAPHICAL), jsonb -> { + String expectedLexicographical = "{\"aField\":\"aValue\",\"bField\":\"bValue\",\"cField\":\"cValue\",\"dField\":\"dValue\"}"; + assertEquals(expectedLexicographical, jsonb.toJson(fieldOrder)); + }); + + testWithJsonbBuilderCreate(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.REVERSE), jsonb -> { + String expectedReverse = "{\"dField\":\"dValue\",\"cField\":\"cValue\",\"bField\":\"bValue\",\"aField\":\"aValue\"}"; + assertEquals(expectedReverse, jsonb.toJson(fieldOrder)); + }); } @Test public void testPropertyCustomOrder() { FieldCustomOrder fieldCustomOrder = new FieldCustomOrder(); - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.LEXICOGRAPHICAL)); - String expectedCustomOrder = "{\"aField\":\"aValue\",\"cField\":\"cValue\",\"dField\":\"dValue\",\"bField\":\"bValue\"}"; - assertEquals(expectedCustomOrder, jsonb.toJson(fieldCustomOrder)); - - FieldCustomOrderWrapper fieldCustomOrderWrapper = new FieldCustomOrderWrapper(); - String expectedOrder = "{\"fieldCustomOrder\":{\"aField\":\"aValue\",\"cField\":\"cValue\",\"dField\":\"dValue\",\"bField\":\"bValue\"},\"intField\":1,\"stringField\":\"stringValue\"}"; - assertEquals(expectedOrder, jsonb.toJson(fieldCustomOrderWrapper)); + testWithJsonbBuilderCreate(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.LEXICOGRAPHICAL), jsonb -> { + String expectedCustomOrder = "{\"aField\":\"aValue\",\"cField\":\"cValue\",\"dField\":\"dValue\",\"bField\":\"bValue\"}"; + assertEquals(expectedCustomOrder, jsonb.toJson(fieldCustomOrder)); + + FieldCustomOrderWrapper fieldCustomOrderWrapper = new FieldCustomOrderWrapper(); + String expectedOrder = + "{\"fieldCustomOrder\":{\"aField\":\"aValue\",\"cField\":\"cValue\",\"dField\":\"dValue\",\"bField\":\"bValue\"},\"intField\":1,\"stringField\":\"stringValue\"}"; + assertEquals(expectedOrder, jsonb.toJson(fieldCustomOrderWrapper)); + }); } @Test public void testPropertySetCustomOrder() { FieldSpecificOrder fieldSpecificOrder = new FieldSpecificOrder(); + testWithJsonbBuilderCreate(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.REVERSE), jsonb -> { String expectedSpecific = "{\"aField\":\"aValue\",\"dField\":\"dValue\",\"bField\":\"bValue\",\"cField\":\"cValue\"}"; assertEquals(expectedSpecific, defaultJsonb.toJson(fieldSpecificOrder)); - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.REVERSE)); - expectedSpecific = "{\"aField\":\"aValue\",\"dField\":\"dValue\",\"cField\":\"cValue\",\"bField\":\"bValue\"}"; - assertEquals(expectedSpecific, jsonb.toJson(fieldSpecificOrder)); + expectedSpecific = "{\"aField\":\"aValue\",\"dField\":\"dValue\",\"cField\":\"cValue\",\"bField\":\"bValue\"}"; + assertEquals(expectedSpecific, jsonb.toJson(fieldSpecificOrder)); + }); } @Test public void testPropertySortingWithNamingAnnotation() { FieldOrderNameAnnotation fieldOrderNameAnnotation = new FieldOrderNameAnnotation(); - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.LEXICOGRAPHICAL)); - String expectedLexicographical = "{\"bField\":\"bValue\",\"cField\":\"cValue\",\"dField\":\"dValue\",\"zField\":\"aValue\"}"; - assertEquals(expectedLexicographical, jsonb.toJson(fieldOrderNameAnnotation)); - - jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.REVERSE)); - String expectedReverse = "{\"zField\":\"aValue\",\"dField\":\"dValue\",\"cField\":\"cValue\",\"bField\":\"bValue\"}"; - assertEquals(expectedReverse, jsonb.toJson(fieldOrderNameAnnotation)); + testWithJsonbBuilderCreate(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.LEXICOGRAPHICAL), jsonb -> { + String expectedLexicographical = "{\"bField\":\"bValue\",\"cField\":\"cValue\",\"dField\":\"dValue\",\"zField\":\"aValue\"}"; + assertEquals(expectedLexicographical, jsonb.toJson(fieldOrderNameAnnotation)); + }); + + testWithJsonbBuilderCreate(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.REVERSE), jsonb -> { + String expectedReverse = "{\"zField\":\"aValue\",\"dField\":\"dValue\",\"cField\":\"cValue\",\"bField\":\"bValue\"}"; + assertEquals(expectedReverse, jsonb.toJson(fieldOrderNameAnnotation)); + }); } @Test public void testLexicographicalPropertyOrderRenamedProperties() { - JsonbConfig config = new JsonbConfig(); - config.setProperty(JsonbConfig.PROPERTY_ORDER_STRATEGY, PropertyOrderStrategy.LEXICOGRAPHICAL); - Jsonb jsonb = JsonbBuilder.create(config); - - String jsonString = jsonb.toJson(new RenamedPropertiesContainer() {{ setStringInstance("Test String"); setLongInstance(1); }}); - assertTrue(jsonString.matches("\\{\\s*\"first\"\\s*\\:\\s*0\\s*,\\s*\"second\"\\s*\\:\\s*\"Test String\"\\s*,\\s*\"third\"\\s*\\:\\s*1\\s*\\}")); - - RenamedPropertiesContainer unmarshalledObject = jsonb.fromJson("{ \"first\" : 1, \"second\" : \"Test String\", \"third\" : 1 }", RenamedPropertiesContainer.class); - assertEquals(3, unmarshalledObject.getIntInstance()); + testWithJsonbBuilderCreate(new JsonbConfig().setProperty(JsonbConfig.PROPERTY_ORDER_STRATEGY, PropertyOrderStrategy.LEXICOGRAPHICAL), jsonb -> { + + String jsonString = jsonb.toJson(new RenamedPropertiesContainer() {{ + setStringInstance("Test String"); + setLongInstance(1); + }}); + assertTrue(jsonString.matches( + "\\{\\s*\"first\"\\s*:\\s*0\\s*,\\s*\"second\"\\s*:\\s*\"Test String\"\\s*,\\s*\"third\"\\s*:\\s*1\\s*}")); + + RenamedPropertiesContainer unmarshalledObject = + jsonb.fromJson("{ \"first\" : 1, \"second\" : \"Test String\", \"third\" : 1 }", RenamedPropertiesContainer.class); + assertEquals(3, unmarshalledObject.getIntInstance()); + }); } @Test @@ -106,7 +115,7 @@ public void testJsonbPropertyOrderOnRenamedProperties() { // But with JsonbPropertyOrder we will put props 'propB' and 'propA' first, and leftovers will go at the end, resulting in: // propB, propA, anExtraProp @JsonbPropertyOrder({"propB","propA"}) - public class Range { + public static class Range { @JsonbProperty("d") public final int propA; diff --git a/src/test/java/org/eclipse/yasson/customization/YassonSpecificConfigTests.java b/src/test/java/org/eclipse/yasson/customization/YassonSpecificConfigTests.java index 4364ebb01..74936f01d 100644 --- a/src/test/java/org/eclipse/yasson/customization/YassonSpecificConfigTests.java +++ b/src/test/java/org/eclipse/yasson/customization/YassonSpecificConfigTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024 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 @@ -14,14 +14,13 @@ import java.util.Optional; -import jakarta.json.bind.Jsonb; -import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.serializer.JsonbSerializer; import jakarta.json.bind.serializer.SerializationContext; import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.YassonConfig; import org.junit.jupiter.api.Test; +import static org.eclipse.yasson.Jsonbs.testWithJsonbBuilderCreate; import static org.junit.jupiter.api.Assertions.assertEquals; /** @@ -34,20 +33,20 @@ public class YassonSpecificConfigTests { @Test public void nullRootSerializerTest() { - Jsonb jsonb = JsonbBuilder.create(new YassonConfig().withNullRootSerializer(new RootNullSerializer())); - assertEquals(NULL_VALUE_SERIALIZED, jsonb.toJson(null)); + testWithJsonbBuilderCreate(new YassonConfig().withNullRootSerializer(new RootNullSerializer()), jsonb -> + assertEquals(NULL_VALUE_SERIALIZED, jsonb.toJson(null))); } @Test public void emptyOptionalRootSerializerTest() { - Jsonb jsonb = JsonbBuilder.create(new YassonConfig().withNullRootSerializer(new RootNullSerializer())); - assertEquals(NULL_VALUE_SERIALIZED, jsonb.toJson(Optional.empty())); + testWithJsonbBuilderCreate(new YassonConfig().withNullRootSerializer(new RootNullSerializer()), jsonb -> + assertEquals(NULL_VALUE_SERIALIZED, jsonb.toJson(Optional.empty()))); } @Test public void nullSerializerNotUsedTest() { - Jsonb jsonb = JsonbBuilder.create(new YassonConfig().withNullRootSerializer(new RootNullSerializer())); - assertEquals("[null]", jsonb.toJson(new String[] {null})); + testWithJsonbBuilderCreate(new YassonConfig().withNullRootSerializer(new RootNullSerializer()), jsonb -> + assertEquals("[null]", jsonb.toJson(new String[] {null}))); } private static final class RootNullSerializer implements JsonbSerializer { @@ -57,5 +56,4 @@ public void serialize(Object obj, JsonGenerator generator, SerializationContext generator.write(NULL_VALUE_STRING); } } - -} +} \ No newline at end of file diff --git a/src/test/java/org/eclipse/yasson/customization/polymorphism/MultiplePolymorphicInfoTest.java b/src/test/java/org/eclipse/yasson/customization/polymorphism/MultiplePolymorphicInfoTest.java index 9bb575a8c..3ff6f1879 100644 --- a/src/test/java/org/eclipse/yasson/customization/polymorphism/MultiplePolymorphicInfoTest.java +++ b/src/test/java/org/eclipse/yasson/customization/polymorphism/MultiplePolymorphicInfoTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024 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 @@ -12,13 +12,12 @@ package org.eclipse.yasson.customization.polymorphism; -import jakarta.json.bind.Jsonb; -import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.annotation.JsonbSubtype; import jakarta.json.bind.annotation.JsonbTypeInfo; import org.junit.jupiter.api.Test; +import static org.eclipse.yasson.Jsonbs.defaultJsonb; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; @@ -28,19 +27,17 @@ */ public class MultiplePolymorphicInfoTest { - private static final Jsonb JSONB = JsonbBuilder.create(); - @Test public void testMultiplePolymorphicInfoPropertySerialization() { String expected = "{\"@something\":\"animal\",\"@animal\":\"dog\",\"@dogRace\":\"labrador\",\"isLabrador\":true}"; Labrador labrador = new Labrador(); - assertThat(JSONB.toJson(labrador), is(expected)); + assertThat(defaultJsonb.toJson(labrador), is(expected)); } @Test public void testMultiplePolymorphicInfoPropertyDeserialization() { String json = "{\"@something\":\"animal\",\"@animal\":\"dog\",\"@dogRace\":\"labrador\",\"isLabrador\":true}"; - assertThat(JSONB.fromJson(json, Labrador.class), instanceOf(Labrador.class)); + assertThat(defaultJsonb.fromJson(json, Labrador.class), instanceOf(Labrador.class)); } @Test @@ -49,13 +46,13 @@ public void testPolymorphicParentInstanceSerialization() { Area northAmerica = new Area(); northAmerica.name = "North America"; northAmerica.population = 600000000; - assertThat(JSONB.toJson(northAmerica), is(expected)); + assertThat(defaultJsonb.toJson(northAmerica), is(expected)); } @Test public void testPolymorphicParentInstanceDeserialization() { String json = "{\"@type\":\"area\",\"name\":\"North America\",\"population\":600000000}"; - assertThat(JSONB.fromJson(json, Location.class), instanceOf(Area.class)); + assertThat(defaultJsonb.fromJson(json, Location.class), instanceOf(Area.class)); } @JsonbTypeInfo(key = "@something", value = { diff --git a/src/test/java/org/eclipse/yasson/customization/polymorphism/PolymorphismWithJsonValuesTest.java b/src/test/java/org/eclipse/yasson/customization/polymorphism/PolymorphismWithJsonValuesTest.java new file mode 100644 index 000000000..128727776 --- /dev/null +++ b/src/test/java/org/eclipse/yasson/customization/polymorphism/PolymorphismWithJsonValuesTest.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2019, 2024 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.customization.polymorphism; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; + +import java.math.BigDecimal; +import java.math.BigInteger; + +import org.eclipse.yasson.Jsonbs; +import org.junit.jupiter.api.Test; + +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.json.JsonValue; +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.annotation.JsonbSubtype; +import jakarta.json.bind.annotation.JsonbTypeInfo; + +public class PolymorphismWithJsonValuesTest { + + /** + * This test shows that the default Jsonb implementation support polymorphism with JsonValues. + * See GitHub issue + */ + @Test + public void testObjectWithJsonValue() { + Jsonb jsonb = Jsonbs.defaultJsonb; + var container = new PolyContainer.JsValueContainer(); + String containerSerialized = jsonb.toJson(container); + assertEquals("{\"type\":\"jsv\",\"jsonArray\":[\"an array json string\"],\"jsonBigDecimalValue\":10,\"jsonBigIntegerValue\":10," + + "\"jsonDoubleValue\":1.0,\"jsonIntValue\":1,\"jsonLongValue\":1,\"jsonObject\":{\"field\":\"an object json string\"}," + + "\"jsonStringValue\":\"a json string\"}", containerSerialized); + + var deserializedDirectly = jsonb.fromJson(containerSerialized, PolyContainer.JsValueContainer.class);//good + assertInstanceOf(PolyContainer.JsValueContainer.class, deserializedDirectly); + assertEquals(container.jsonStringValue, deserializedDirectly.jsonStringValue); + assertEquals(container.jsonIntValue, deserializedDirectly.jsonIntValue); + assertEquals(container.jsonLongValue, deserializedDirectly.jsonLongValue); + assertEquals(container.jsonDoubleValue, deserializedDirectly.jsonDoubleValue); + assertEquals(container.jsonBigDecimalValue, deserializedDirectly.jsonBigDecimalValue); + assertEquals(container.jsonBigIntegerValue, deserializedDirectly.jsonBigIntegerValue); + assertEquals(container.jsonArray, deserializedDirectly.jsonArray); + assertEquals(container.jsonObject, deserializedDirectly.jsonObject); + + var deserializedFromPoly = jsonb.fromJson(containerSerialized, PolyContainer.class);//bad + assertInstanceOf(PolyContainer.JsValueContainer.class, deserializedFromPoly); + assertEquals(container.jsonStringValue, ((PolyContainer.JsValueContainer)deserializedFromPoly).jsonStringValue); + assertEquals(container.jsonIntValue, ((PolyContainer.JsValueContainer)deserializedFromPoly).jsonIntValue); + assertEquals(container.jsonLongValue, ((PolyContainer.JsValueContainer)deserializedFromPoly).jsonLongValue); + assertEquals(container.jsonDoubleValue, ((PolyContainer.JsValueContainer)deserializedFromPoly).jsonDoubleValue); + assertEquals(container.jsonBigDecimalValue, ((PolyContainer.JsValueContainer)deserializedFromPoly).jsonBigDecimalValue); + assertEquals(container.jsonBigIntegerValue, ((PolyContainer.JsValueContainer)deserializedFromPoly).jsonBigIntegerValue); + assertEquals(container.jsonArray, ((PolyContainer.JsValueContainer)deserializedFromPoly).jsonArray); + assertEquals(container.jsonObject, ((PolyContainer.JsValueContainer)deserializedFromPoly).jsonObject); + } + + @JsonbTypeInfo( + key = "type", + value = {@JsonbSubtype(alias = "jsv", type = PolyContainer.JsValueContainer.class)} + ) + interface PolyContainer { + class JsValueContainer implements PolyContainer { + //fields should not be final, otherwise the test couldn't test + public /*final*/ JsonValue jsonStringValue = Json.createValue("a json string"); + public /*final*/ JsonValue jsonIntValue = Json.createValue(1); + public /*final*/ JsonValue jsonLongValue = Json.createValue(1L); + public /*final*/ JsonValue jsonDoubleValue = Json.createValue(1d); + public /*final*/ JsonValue jsonBigDecimalValue = Json.createValue(BigDecimal.TEN); + public /*final*/ JsonValue jsonBigIntegerValue = Json.createValue(BigInteger.TEN); + public /*final*/ JsonArray jsonArray = Json.createArrayBuilder().add("an array json string").build(); + public /*final*/ JsonObject jsonObject = Json.createObjectBuilder().add("field", "an object json string").build(); + } + } +} \ No newline at end of file diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/IJsonTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/IJsonTest.java index de93f7b71..172feddad 100644 --- a/src/test/java/org/eclipse/yasson/defaultmapping/IJsonTest.java +++ b/src/test/java/org/eclipse/yasson/defaultmapping/IJsonTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024 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 @@ -31,13 +31,13 @@ */ public class IJsonTest { - private Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withStrictIJSON(true)); + private final static Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withStrictIJSON(true)); @Test public void testStrictCalendar() { Calendar calendar = Calendar.getInstance(); calendar.clear(); - calendar.set(1970, 0, 1, 0, 0, 0); + calendar.set(1970, Calendar.JANUARY, 1, 0, 0, 0); calendar.setTimeZone(TimeZone.getTimeZone("Europe/Paris")); String jsonString = jsonb.toJson(new ScalarValueWrapper<>(calendar)); @@ -51,7 +51,7 @@ public void testStrictCalendar() { @Test public void testStrictDate() { Calendar calendar = Calendar.getInstance(); - calendar.set(1970, 0, 1, 0, 0, 0); + calendar.set(1970, Calendar.JANUARY, 1, 0, 0, 0); calendar.setTimeZone(TimeZone.getTimeZone("UTC")); calendar.clear(Calendar.MILLISECOND); diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/SecurityManagerTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/SecurityManagerTest.java index 029e55402..05eeda01a 100644 --- a/src/test/java/org/eclipse/yasson/defaultmapping/SecurityManagerTest.java +++ b/src/test/java/org/eclipse/yasson/defaultmapping/SecurityManagerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024 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 @@ -12,12 +12,12 @@ package org.eclipse.yasson.defaultmapping; +import static org.eclipse.yasson.Jsonbs.testWithJsonbBuilderCreate; + import org.junit.jupiter.api.*; import org.eclipse.yasson.serializers.model.Crate; -import jakarta.json.bind.Jsonb; -import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.annotation.JsonbProperty; import jakarta.json.bind.config.PropertyVisibilityStrategy; @@ -48,7 +48,7 @@ public static void tearDown() { @Test public void testWithSecurityManager() { - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyVisibilityStrategy(new PropertyVisibilityStrategy() { + JsonbConfig config = new JsonbConfig().withPropertyVisibilityStrategy(new PropertyVisibilityStrategy() { @Override public boolean isVisible(Field field) { return Modifier.isPublic(field.getModifiers()) || field.getName().equals("privateProperty"); @@ -58,16 +58,18 @@ public boolean isVisible(Field field) { public boolean isVisible(Method method) { return Modifier.isPublic(method.getModifiers()); } - })); - - Pojo pojo = new Pojo(); - pojo.setStrProperty("string propery"); - Crate crate = new Crate(); - crate.crateBigDec = BigDecimal.TEN; - crate.crateStr = "crate string"; - pojo.setCrate(crate); - - String result = jsonb.toJson(pojo); + }); + + testWithJsonbBuilderCreate(config, jsonb -> { + Pojo pojo = new Pojo(); + pojo.setStrProperty("string propery"); + Crate crate = new Crate(); + crate.crateBigDec = BigDecimal.TEN; + crate.crateStr = "crate string"; + pojo.setCrate(crate); + + String result = jsonb.toJson(pojo); + }); } diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/collections/CollectionsTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/collections/CollectionsTest.java index 0e7fdb422..86b2ad327 100644 --- a/src/test/java/org/eclipse/yasson/defaultmapping/collections/CollectionsTest.java +++ b/src/test/java/org/eclipse/yasson/defaultmapping/collections/CollectionsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024 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 @@ -38,6 +38,8 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentNavigableMap; import java.util.concurrent.ConcurrentSkipListMap; +import java.util.stream.Collectors; +import java.util.stream.IntStream; import org.eclipse.yasson.TestTypeToken; import org.eclipse.yasson.defaultmapping.generics.model.Circle; @@ -86,6 +88,7 @@ public void testListOfNumbers() { numberList.add(10); String result = nullableJsonb.toJson(numberList, new TestTypeToken>(){}.getType()); + assertEquals("[1,2.0,10]", result); } @Test @@ -107,26 +110,14 @@ public void testListOfListsOfStrings() { @Test public void listOfMapsOfListsOfMaps() { - List>>> listOfMapsOfListsOfMaps = new ArrayList<>(); - - for(int i = 0; i < 3; i++) { - Map>> mapOfListsOfMap = new HashMap<>(); - - for(int j = 0; j < 3; j++) { - List> listOfMaps = new ArrayList<>(); - - for(int k = 0; k < 3; k++) { - Map stringIntegerMap = new HashMap<>(); - stringIntegerMap.put("first", 1); - stringIntegerMap.put("second", 2); - stringIntegerMap.put("third", 3); - listOfMaps.add(stringIntegerMap); - } - mapOfListsOfMap.put(String.valueOf(j), listOfMaps); - } - listOfMapsOfListsOfMaps.add(mapOfListsOfMap); - } - + List>>> listOfMapsOfListsOfMaps = IntStream.range(0, 3).mapToObj(i -> + IntStream.range(0, 3).boxed().collect(Collectors.toMap(String::valueOf, j -> + IntStream.range(0, 3).mapToObj(k -> + new HashMap<>(Map.of("first", 1, "second", 2, "third", 3)) + ).collect(Collectors.toList()) + )) + ).collect(Collectors.toList()); + String expected = "[{\"0\":[{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2}],\"1\":[{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2}],\"2\":[{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2}]},{\"0\":[{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2}],\"1\":[{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2}],\"2\":[{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2}]},{\"0\":[{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2}],\"1\":[{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2}],\"2\":[{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2}]}]"; assertEquals(expected, nullableJsonb.toJson(listOfMapsOfListsOfMaps)); ArrayList>>> result = nullableJsonb.fromJson(expected, new TestTypeToken>>>>(){}.getType()); @@ -199,6 +190,7 @@ public void testMarshallArray() { final String[][] stringMultiArray = {{"first", "second"},{"third", "fourth"}}; assertEquals("[[\"first\",\"second\"],[\"third\",\"fourth\"]]", nullableJsonb.toJson(stringMultiArray)); + @SuppressWarnings("rawtypes") final Map[][] mapMultiArray = new LinkedHashMap[2][2]; mapMultiArray[0][0] = new LinkedHashMap<>(1); mapMultiArray[0][0].put("0", 0); @@ -233,9 +225,8 @@ public void testMarshallEnumMap() { } @Test - @SuppressWarnings("unchecked") public void testRawCollection() { - List rawList = new ArrayList(); + List rawList = new ArrayList<>(); rawList.add("first"); Circle circle = new Circle(); circle.setRadius(2.0); @@ -244,10 +235,10 @@ public void testRawCollection() { String expected = "[\"first\",{\"area\":1.0,\"radius\":2.0}]"; assertEquals(expected, nullableJsonb.toJson(rawList)); - List result = nullableJsonb.fromJson(expected, List.class); + List result = nullableJsonb.fromJson(expected, List.class); assertEquals("first", result.get(0)); - assertEquals(new BigDecimal("2.0"), ((Map)result.get(1)).get("radius")); - assertEquals(new BigDecimal("1.0"), ((Map)result.get(1)).get("area")); + assertEquals(new BigDecimal("2.0"), ((Map)result.get(1)).get("radius")); + assertEquals(new BigDecimal("1.0"), ((Map)result.get(1)).get("area")); } @Test @@ -289,7 +280,7 @@ public static class ConcurrentMapContainer { public void testConcurrentMaps() { // ConcurrentMap ConcurrentMapContainer c = new ConcurrentMapContainer(); - c.concurrentMap = new ConcurrentHashMap(); + c.concurrentMap = new ConcurrentHashMap<>(); c.concurrentMap.put("foo", "fooVal"); c.concurrentMap.put("bar", "barVal"); String expectedJson = "{\"concurrentMap\":{\"bar\":\"barVal\",\"foo\":\"fooVal\"}}"; @@ -298,7 +289,7 @@ public void testConcurrentMaps() { // ConcurrentHashMap c = new ConcurrentMapContainer(); - c.concurrentHashMap = new ConcurrentHashMap(); + c.concurrentHashMap = new ConcurrentHashMap<>(); c.concurrentHashMap.put("foo", "fooVal2"); c.concurrentHashMap.put("bar", "barVal2"); expectedJson = "{\"concurrentHashMap\":{\"bar\":\"barVal2\",\"foo\":\"fooVal2\"}}"; @@ -307,7 +298,7 @@ public void testConcurrentMaps() { // ConcurrentNavigableMap c = new ConcurrentMapContainer(); - c.concurrentNavigableMap = new ConcurrentSkipListMap(); + c.concurrentNavigableMap = new ConcurrentSkipListMap<>(); c.concurrentNavigableMap.put("foo", "fooVal3"); c.concurrentNavigableMap.put("bar", "barVal3"); expectedJson = "{\"concurrentNavigableMap\":{\"bar\":\"barVal3\",\"foo\":\"fooVal3\"}}"; @@ -316,7 +307,7 @@ public void testConcurrentMaps() { // ConcurrentSkipListMap c = new ConcurrentMapContainer(); - c.concurrentSkipListMap = new ConcurrentSkipListMap(); + c.concurrentSkipListMap = new ConcurrentSkipListMap<>(); c.concurrentSkipListMap.put("foo", "fooVal4"); c.concurrentSkipListMap.put("bar", "barVal4"); expectedJson = "{\"concurrentSkipListMap\":{\"bar\":\"barVal4\",\"foo\":\"fooVal4\"}}"; diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/dates/DatesTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/dates/DatesTest.java index 287afca87..919dad349 100644 --- a/src/test/java/org/eclipse/yasson/defaultmapping/dates/DatesTest.java +++ b/src/test/java/org/eclipse/yasson/defaultmapping/dates/DatesTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024 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 @@ -46,8 +46,6 @@ import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.XMLGregorianCalendar; -import jakarta.json.bind.Jsonb; -import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.annotation.JsonbDateFormat; import jakarta.json.bind.annotation.JsonbTypeDeserializer; @@ -73,6 +71,7 @@ import static org.eclipse.yasson.Jsonbs.bindingJsonb; import static org.eclipse.yasson.Jsonbs.defaultJsonb; +import static org.eclipse.yasson.Jsonbs.testWithJsonbBuilderCreate; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; @@ -85,13 +84,15 @@ public class DatesTest { private static LocalDate localDate = LocalDate.of(2018, 1, 31); - @SuppressWarnings("serial") public static class LocalDateObj implements Serializable { + private static final long serialVersionUID = 1L; + public LocalDate date = localDate; } - @SuppressWarnings("serial") public static class SqlDateObj implements Serializable { + private static final long serialVersionUID = 1L; + public java.sql.Date sqlDate = java.sql.Date.valueOf("2018-01-31"); //no way for runtime to choose java.sql.Date deserializer here without a hint @JsonbTypeDeserializer(SqlDateDeserializer.class) @@ -138,11 +139,12 @@ private void testSqlDateWithTZFormatted(TimeZone tz) { final TimeZone originalTZ = TimeZone.getDefault(); TimeZone.setDefault(tz); try { - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withDateFormat("yyyy-MM-dd", Locale.getDefault())); - java.sql.Date d = java.sql.Date.valueOf("1966-11-04"); - String json = jsonb.toJson(d); - assertEquals("\"1966-11-04\"", json); - assertEquals("1966-11-04", jsonb.fromJson(json, java.sql.Date.class).toString()); + testWithJsonbBuilderCreate(new JsonbConfig().withDateFormat("yyyy-MM-dd", Locale.getDefault()), (jsonb) -> { + java.sql.Date d = java.sql.Date.valueOf("1966-11-04"); + String json = jsonb.toJson(d); + assertEquals("\"1966-11-04\"", json); + assertEquals("1966-11-04", jsonb.fromJson(json, java.sql.Date.class).toString()); + }); } finally { TimeZone.setDefault(originalTZ); } @@ -158,14 +160,13 @@ public void testSqlDateTimeZonesMillis() { private void testSqlDateTimeZonesMillis(TimeZone tz, long expectedMs) { final TimeZone originalTZ = TimeZone.getDefault(); TimeZone.setDefault(tz); - JsonbConfig jsonbConfig = new JsonbConfig().withDateFormat(JsonbDateFormat.TIME_IN_MILLIS, Locale.getDefault()); - try (Jsonb jsonb = JsonbBuilder.create(jsonbConfig)) { - java.sql.Date d = java.sql.Date.valueOf("1966-11-04"); - String json = jsonb.toJson(d); - assertEquals("" + expectedMs, json); - assertEquals(d, jsonb.fromJson(json, java.sql.Date.class)); - } catch (Exception e) { - throw new RuntimeException(e); + try { + testWithJsonbBuilderCreate(new JsonbConfig().withDateFormat(JsonbDateFormat.TIME_IN_MILLIS, Locale.getDefault()), (jsonb) -> { + java.sql.Date d = java.sql.Date.valueOf("1966-11-04"); + String json = jsonb.toJson(d); + assertEquals("" + expectedMs, json); + assertEquals(d, jsonb.fromJson(json, java.sql.Date.class)); + }); } finally { TimeZone.setDefault(originalTZ); } @@ -358,19 +359,20 @@ public void testMarshalInstant() { @Test public void testDateFormattedAsMillisInString() { - Jsonb jsonb = JsonbBuilder.create(new YassonConfig().withTimeInMillisAsAString(true)); - final Instant instant = Instant.parse("2015-03-03T23:00:00Z"); - InstantPojo instantPojo = new InstantPojo(instant); - - final String expected = "{\"defaultFormatted\":\"2015-03-03T23:00:00Z\"," + - "\"millisFormatted\":\"1425423600000\"," + - "\"instant\":\"23:00:00 | 03-03-2015\"}"; - assertEquals(expected, jsonb.toJson(instantPojo)); - - InstantPojo result = jsonb.fromJson(expected, InstantPojo.class); - assertEquals(instant, result.defaultFormatted); - assertEquals(instant, result.millisFormatted); - assertEquals(instant, result.instant); + testWithJsonbBuilderCreate(new YassonConfig().withTimeInMillisAsAString(true), jsonb -> { + final Instant instant = Instant.parse("2015-03-03T23:00:00Z"); + InstantPojo instantPojo = new InstantPojo(instant); + + final String expected = "{\"defaultFormatted\":\"2015-03-03T23:00:00Z\"," + + "\"millisFormatted\":\"1425423600000\"," + + "\"instant\":\"23:00:00 | 03-03-2015\"}"; + assertEquals(expected, jsonb.toJson(instantPojo)); + + InstantPojo result = jsonb.fromJson(expected, InstantPojo.class); + assertEquals(instant, result.defaultFormatted); + assertEquals(instant, result.millisFormatted); + assertEquals(instant, result.instant); + }); } @Test @@ -406,20 +408,20 @@ public void testLocalDate() { @Test public void testlLocalTime() { - final Jsonb jsonb = getJsonbWithMillisIgnored(); - final LocalTime localTime = LocalTime.of(22, 33); - final LocalTimePojo localTimePojo = new LocalTimePojo(localTime); - final String expected = "{\"defaultFormatted\":\"22:33:00\",\"localTime\":\"22:33:00\"}"; - assertEquals(expected, jsonb.toJson(localTimePojo)); - - final LocalTimePojo result = jsonb.fromJson(expected, LocalTimePojo.class); - assertEquals(localTime, result.defaultFormatted); - assertEquals(localTime, result.localTime); + testWithJsonbBuilderCreate(getJsonbConfigWithMillisIgnored(), jsonb -> { + final LocalTime localTime = LocalTime.of(22, 33); + final LocalTimePojo localTimePojo = new LocalTimePojo(localTime); + final String expected = "{\"defaultFormatted\":\"22:33:00\",\"localTime\":\"22:33:00\"}"; + assertEquals(expected, jsonb.toJson(localTimePojo)); + + final LocalTimePojo result = jsonb.fromJson(expected, LocalTimePojo.class); + assertEquals(localTime, result.defaultFormatted); + assertEquals(localTime, result.localTime); + }); } - private Jsonb getJsonbWithMillisIgnored() { - final JsonbConfig config = new JsonbConfig(); - config.withPropertyVisibilityStrategy(new PropertyVisibilityStrategy() { + private JsonbConfig getJsonbConfigWithMillisIgnored() { + return new JsonbConfig().withPropertyVisibilityStrategy(new PropertyVisibilityStrategy() { @Override public boolean isVisible(Field field) { return !field.getName().startsWith("millis"); @@ -430,7 +432,6 @@ public boolean isVisible(Method method) { return false; } }); - return JsonbBuilder.create(config); } @Test @@ -475,14 +476,17 @@ public void testDifferentConfigsLocalDateTime() { final String expected = "{\"value\":\"2015-02-16T13:21:00\"}"; assertEquals(expected, bindingJsonb.toJson(pojo)); - final Jsonb jsonbCustom = JsonbBuilder.create(new JsonbConfig().withDateFormat(JsonbDateFormat.TIME_IN_MILLIS, Locale.FRENCH)); - assertEquals("{\"value\":" + millis + "}", jsonbCustom.toJson(pojo)); + testWithJsonbBuilderCreate(new JsonbConfig().withDateFormat(JsonbDateFormat.TIME_IN_MILLIS, Locale.FRENCH), jsonb -> { + assertEquals("{\"value\":" + millis + "}", jsonb.toJson(pojo)); - ScalarValueWrapper result = bindingJsonb.fromJson(expected, new TestTypeToken>(){}.getType()); - assertEquals(dateTime, result.getValue()); + ScalarValueWrapper result = bindingJsonb.fromJson(expected, new TestTypeToken>() { + }.getType()); + assertEquals(dateTime, result.getValue()); - result = jsonbCustom.fromJson("{\"value\":\"" + millis + "\"}", new TestTypeToken>(){}.getType()); - assertEquals(dateTime, result.getValue()); + result = jsonb.fromJson("{\"value\":\"" + millis + "\"}", new TestTypeToken>() { + }.getType()); + assertEquals(dateTime, result.getValue()); + }); } @Test @@ -533,16 +537,17 @@ public void testMarshalOffsetDateTime() { @Test public void testMarshalOffsetTime() { - final Jsonb jsonb = getJsonbWithMillisIgnored(); - final OffsetTime dateTime = OffsetTime.of(13, 21, 15, 0, ZoneOffset.of("+05:00")); - final OffsetTimePojo pojo = new OffsetTimePojo(dateTime); + testWithJsonbBuilderCreate(getJsonbConfigWithMillisIgnored(), jsonb -> { + final OffsetTime dateTime = OffsetTime.of(13, 21, 15, 0, ZoneOffset.of("+05:00")); + final OffsetTimePojo pojo = new OffsetTimePojo(dateTime); - final String expected = "{\"defaultFormatted\":\"13:21:15+05:00\",\"offsetTime\":\"13:21:15+0500\"}"; - assertEquals(expected, jsonb.toJson(pojo)); + final String expected = "{\"defaultFormatted\":\"13:21:15+05:00\",\"offsetTime\":\"13:21:15+0500\"}"; + assertEquals(expected, jsonb.toJson(pojo)); - final OffsetTimePojo result = jsonb.fromJson(expected, OffsetTimePojo.class); - assertEquals(dateTime, result.defaultFormatted); - assertEquals(dateTime, result.offsetTime); + final OffsetTimePojo result = jsonb.fromJson(expected, OffsetTimePojo.class); + assertEquals(dateTime, result.defaultFormatted); + assertEquals(dateTime, result.offsetTime); + }); } @Test @@ -574,30 +579,32 @@ public void testClassLevel() throws ParseException { @Test public void testGlobalConfigDateFormat() { - final JsonbConfig config = new JsonbConfig(); - config.withDateFormat("X z E MMMM dd-MM-yyyy HH:mm:ss", Locale.FRENCH); - - final Jsonb jsonb = JsonbBuilder.create(config); + testWithJsonbBuilderCreate(new JsonbConfig().withDateFormat("X z E MMMM dd-MM-yyyy HH:mm:ss", Locale.FRENCH), jsonb -> { - final ZonedDateTime dateTime = ZonedDateTime.of(2015, 4, 3, 13, 21, 0, 0, ZoneId.of("Asia/Almaty")); - final String expected = "{\"value\":\"+06 ALMT ven. avril 03-04-2015 13:21:00\"}"; - assertEquals(expected, jsonb.toJson(new ScalarValueWrapper<>(dateTime))); + final ZonedDateTime dateTime = ZonedDateTime.of(2015, 4, 3, 13, 21, 0, 0, ZoneId.of("Asia/Almaty")); + final String expected = "{\"value\":\"+06 ALMT ven. avril 03-04-2015 13:21:00\"}"; + assertEquals(expected, jsonb.toJson(new ScalarValueWrapper<>(dateTime))); - final ScalarValueWrapper result = jsonb.fromJson(expected, new TestTypeToken>(){}.getType()); - assertEquals(dateTime, result.getValue()); + final ScalarValueWrapper result = jsonb.fromJson(expected, new TestTypeToken>() { + }.getType()); + assertEquals(dateTime, result.getValue()); + }); } @Test public void testDateFrenchLocale() { String format = "E DD MMM yyyy HH:mm:ss z"; Locale locale = Locale.forLanguageTag("fr-FR"); - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withDateFormat(format, locale)); + testWithJsonbBuilderCreate(new JsonbConfig().withDateFormat(format, locale), jsonb -> { - final ScalarValueWrapper result = jsonb.fromJson("{ \"value\" : \"lun. 93 avr. 2017 16:51:12 CEST\" }", new TestTypeToken>(){}.getType()); + final ScalarValueWrapper result = + jsonb.fromJson("{ \"value\" : \"lun. 93 avr. 2017 16:51:12 CEST\" }", new TestTypeToken>() { + }.getType()); - DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format); - final Instant instant = Instant.from(formatter.withLocale(locale).parse("lun. 93 avr. 2017 16:51:12 CEST")); - assertEquals(instant, result.getValue().toInstant()); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format); + final Instant instant = Instant.from(formatter.withLocale(locale).parse("lun. 93 avr. 2017 16:51:12 CEST")); + assertEquals(instant, result.getValue().toInstant()); + }); } @Test @@ -614,28 +621,24 @@ public void testSimpleTimeZone() { @Test public void testDateInMap() { + testWithJsonbBuilderCreate(new JsonbConfig().withDateFormat("yyyy", Locale.ENGLISH), jsonb -> { + LocalDate localDate = LocalDate.of(2017, 9, 14); - JsonbConfig config = new JsonbConfig() - .withDateFormat("yyyy", Locale.ENGLISH); - - Jsonb jsonb = JsonbBuilder.create(config); - LocalDate localDate = LocalDate.of(2017, 9, 14); + DateInMapPojo pojo = new DateInMapPojo(); + pojo.setLocalDate(localDate); + pojo.setDateMap(new HashMap<>()); + pojo.getDateMap().put("first", localDate); - DateInMapPojo pojo = new DateInMapPojo(); - pojo.setLocalDate(localDate); - pojo.setDateMap(new HashMap<>()); - pojo.getDateMap().put("first", localDate); + String json = jsonb.toJson(pojo); - String json = jsonb.toJson(pojo); - - assertEquals("{\"dateMap\":{\"first\":\"2017\"},\"localDate\":\"2017\"}", json); + assertEquals("{\"dateMap\":{\"first\":\"2017\"},\"localDate\":\"2017\"}", json); + }); - config = new JsonbConfig() - .withDateFormat("dd.MM.yyyy", Locale.ENGLISH); - jsonb = JsonbBuilder.create(config); - DateInMapPojo result = jsonb.fromJson("{\"dateMap\":{\"first\":\"01.01.2017\"},\"localDate\":\"01.01.2017\"}", DateInMapPojo.class); - assertEquals(LocalDate.of(2017,1,1), result.localDate); - assertEquals(LocalDate.of(2017,1,1), result.dateMap.get("first")); + testWithJsonbBuilderCreate(new JsonbConfig().withDateFormat("dd.MM.yyyy", Locale.ENGLISH), jsonb -> { + DateInMapPojo result = jsonb.fromJson("{\"dateMap\":{\"first\":\"01.01.2017\"},\"localDate\":\"01.01.2017\"}", DateInMapPojo.class); + assertEquals(LocalDate.of(2017, 1, 1), result.localDate); + assertEquals(LocalDate.of(2017, 1, 1), result.dateMap.get("first")); + }); } @Test diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/generics/GenericsTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/generics/GenericsTest.java index a1bf7f2b4..88f2a0c6d 100644 --- a/src/test/java/org/eclipse/yasson/defaultmapping/generics/GenericsTest.java +++ b/src/test/java/org/eclipse/yasson/defaultmapping/generics/GenericsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024 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 @@ -12,6 +12,13 @@ package org.eclipse.yasson.defaultmapping.generics; +import static org.eclipse.yasson.Jsonbs.defaultJsonb; +import static org.eclipse.yasson.Jsonbs.testWithJsonbBuilderCreate; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.lang.reflect.Field; import java.lang.reflect.Type; import java.math.BigDecimal; import java.text.ParseException; @@ -26,11 +33,6 @@ import java.util.Optional; import java.util.TimeZone; -import jakarta.json.bind.Jsonb; -import jakarta.json.bind.JsonbBuilder; -import jakarta.json.bind.JsonbConfig; -import java.lang.reflect.Field; -import java.util.Collection; import org.eclipse.yasson.TestTypeToken; import org.eclipse.yasson.adapters.model.GenericBox; import org.eclipse.yasson.defaultmapping.generics.model.AnotherGenericTestClass; @@ -44,6 +46,7 @@ import org.eclipse.yasson.defaultmapping.generics.model.GenericArrayClass; import org.eclipse.yasson.defaultmapping.generics.model.GenericTestClass; import org.eclipse.yasson.defaultmapping.generics.model.GenericWithUnboundedWildcardClass; +import org.eclipse.yasson.defaultmapping.generics.model.LowerBoundTypeVariableWithCollectionAttributeClass; import org.eclipse.yasson.defaultmapping.generics.model.MultiLevelExtendedGenericTestClass; import org.eclipse.yasson.defaultmapping.generics.model.MultipleBoundsContainer; import org.eclipse.yasson.defaultmapping.generics.model.MyCyclicGenericClass; @@ -55,11 +58,7 @@ import org.eclipse.yasson.serializers.model.Crate; import org.junit.jupiter.api.Test; -import static org.eclipse.yasson.Jsonbs.defaultJsonb; -import org.eclipse.yasson.defaultmapping.generics.model.LowerBoundTypeVariableWithCollectionAttributeClass; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; +import jakarta.json.bind.JsonbConfig; /** * This class contains JSONB default mapping generics tests. @@ -171,9 +170,9 @@ public void testGenericWithUnboundedWildcard() { assertEquals(expected, defaultJsonb.toJson(genericWithUnboundedWildcardClass)); GenericWithUnboundedWildcardClass result = defaultJsonb.fromJson(expected, GenericWithUnboundedWildcardClass.class); - assertTrue(result.wildcardList.get(0) instanceof Map); - assertEquals("v1", ((Map) result.wildcardList.get(0)).get("k1")); - assertEquals("v2", ((Map) result.wildcardList.get(0)).get("k2")); + assertInstanceOf(Map.class, result.wildcardList.get(0)); + assertEquals("v1", ((Map) result.wildcardList.get(0)).get("k1")); + assertEquals("v2", ((Map) result.wildcardList.get(0)).get("k2")); } @Test @@ -270,19 +269,7 @@ public void testMarshallPropagatedGenericsRaw() { @Test public void testFunctional() { - FunctionalInterface myFunction = new FunctionalInterface() { - - private String value = "initValue"; - - @Override - public String getValue() { - return value; - } - - public void setValue(String value) { - this.value = value; - } - }; + FunctionalInterface myFunction = () -> "initValue"; assertEquals("{\"value\":\"initValue\"}", defaultJsonb.toJson(myFunction)); } @@ -311,18 +298,20 @@ public void testBoundedGenerics() { String expected = "{\"boundedSet\":[3],\"lowerBoundedList\":[{\"radius\":2.5}],\"upperBoundedList\":[{\"radius\":3.5,\"color\":\"0,0,255\"}]}"; assertEquals(expected, defaultJsonb.toJson(boundedGenericClass)); - Jsonb localJsonb = JsonbBuilder.create(new JsonbConfig()); - BoundedGenericClass, Circle> result = localJsonb.fromJson(expected, - new TestTypeToken, Circle>>(){}.getType()); - assertEquals(Circle.class, result.lowerBoundedList.get(0).getClass()); - assertEquals(Double.valueOf(2.5), ((Circle) result.lowerBoundedList.get(0)).getRadius()); - - //There is no way of identifying precise class (ColoredCircle) during json unmarshalling. - //Fields that are missing in upper bounds are skipped. - assertEquals(Circle.class, result.upperBoundedList.get(0).getClass()); - assertEquals(Double.valueOf(3.5), result.upperBoundedList.get(0).getRadius()); - //If it was possible we could assert following, but it is not. - //assertEquals("0,0,255", ((ColoredCircle) result.upperBoundedList.get(0)).color); + testWithJsonbBuilderCreate(new JsonbConfig(), localJsonb -> { + BoundedGenericClass, Circle> result = localJsonb.fromJson(expected, + new TestTypeToken, Circle>>() { + }.getType()); + assertEquals(Circle.class, result.lowerBoundedList.get(0).getClass()); + assertEquals(Double.valueOf(2.5), ((Circle) result.lowerBoundedList.get(0)).getRadius()); + + //There is no way of identifying precise class (ColoredCircle) during json unmarshalling. + //Fields that are missing in upper bounds are skipped. + assertEquals(Circle.class, result.upperBoundedList.get(0).getClass()); + assertEquals(Double.valueOf(3.5), result.upperBoundedList.get(0).getRadius()); + //If it was possible we could assert following, but it is not. + //assertEquals("0,0,255", ((ColoredCircle) result.upperBoundedList.get(0)).color); + }); } @Test @@ -332,7 +321,7 @@ public void testIncompatibleTypes() { BoundedGenericClass, Circle> otherGeneric = defaultJsonb.fromJson("{\"boundedSet\":[3],\"lowerBoundedList\":[{\"radius\":2.5}]}", new TestTypeToken, Circle>>(){}.getType()); HashSet otherIntSet = otherGeneric.boundedSet; - Integer intValue = otherIntSet.iterator().next(); + Integer intValue = otherIntSet.iterator().next(); //need to have a variable for the exception to be thrown }); } @@ -382,9 +371,8 @@ public void testGenericArray() { } @Test - @SuppressWarnings("unchecked") public void testMarshallRawList() throws ParseException { - List rawList = new ArrayList(); + List rawList = new ArrayList<>(); final SimpleDateFormat ddMMyyyy = new SimpleDateFormat("ddMMyyyy"); ddMMyyyy.setTimeZone(TimeZone.getTimeZone("Z")); rawList.add(ddMMyyyy.parse("24031981")); @@ -401,17 +389,17 @@ public void testMarshallRawList() throws ParseException { @Test public void testMultipleBounds() { final LinkedList list = new LinkedList<>(Arrays.asList("Test 1", "Test 2")); - MultipleBoundsContainer> container = new MultipleBoundsContainer<>(); + MultipleBoundsContainer> container = new MultipleBoundsContainer<>(); container.setInstance(new ArrayList<>()); container.getInstance().add(list); - final Type type = new TestTypeToken>>() { + final Type type = new TestTypeToken>>() { }.getType(); String jsonString = defaultJsonb.toJson(container, type); assertEquals("{\"instance\":[[\"Test 1\",\"Test 2\"]]}", jsonString); - MultipleBoundsContainer> result = defaultJsonb.fromJson(jsonString, type); + MultipleBoundsContainer> result = defaultJsonb.fromJson(jsonString, type); assertEquals(container.getInstance(), result.getInstance()); } @@ -420,7 +408,7 @@ public void testMultipleBounds() { @SuppressWarnings("unchecked") public void testDeserializeIntoRaw() { - GenericTestClass result = defaultJsonb.fromJson("{\"field1\":{\"val1\":\"abc\"},\"field2\":{\"val1\":\"def\"}}", GenericTestClass.class); + GenericTestClass result = defaultJsonb.fromJson("{\"field1\":{\"val1\":\"abc\"},\"field2\":{\"val1\":\"def\"}}", GenericTestClass.class); assertEquals(((HashMap) result.getField1()).get("val1"), "abc"); assertEquals(((HashMap) result.getField2()).get("val1"), "def"); } @@ -432,7 +420,7 @@ public void collectionWrapperTest() { collectionWrapper.setWrappedCollection(new ArrayList<>()); collectionWrapper.setWrappedMap(new HashMap<>()); - String s = defaultJsonb.toJson(collectionWrapper); + defaultJsonb.toJson(collectionWrapper); } @Test @@ -443,9 +431,8 @@ public void multipleGenericLevels() { concreteContainer.setMember(member); String expected = "{\"member\":{\"name\":\"Jason\"}}"; - Jsonb jsonb = JsonbBuilder.create(); - assertEquals(expected, jsonb.toJson(concreteContainer)); - FinalGenericWrapper finalGenericWrapper = jsonb.fromJson(expected, FinalGenericWrapper.class); + assertEquals(expected, defaultJsonb.toJson(concreteContainer)); + FinalGenericWrapper finalGenericWrapper = defaultJsonb.fromJson(expected, FinalGenericWrapper.class); assertEquals(concreteContainer, finalGenericWrapper); } @@ -459,20 +446,18 @@ public void lowerBoundTypeVariableInCollectionAttribute() throws Exception { anotherGenericTestClass.field1 = 6; anotherGenericTestClass.field2 = shape; - List> asList = Arrays.asList(anotherGenericTestClass); - - Jsonb jsonb = JsonbBuilder.create(); - String toJson = jsonb.toJson(asList); + List> asList = List.of(anotherGenericTestClass); + String toJson = defaultJsonb.toJson(asList); + Field field = LowerBoundTypeVariableWithCollectionAttributeClass.class.getDeclaredField("value"); - + Type genericType = field.getGenericType(); - - List> fromJson = jsonb.fromJson(toJson, genericType); + + List> fromJson = defaultJsonb.fromJson(toJson, genericType); assertEquals(5, fromJson.get(0).field2.getArea()); assertEquals(6, fromJson.get(0).field1); - } public interface FunctionalInterface { @@ -480,6 +465,7 @@ public interface FunctionalInterface { } public static class ExtendsBigDecimal extends BigDecimal { + private static final long serialVersionUID = 1L; public ExtendsBigDecimal(String val) { super(val); @@ -487,4 +473,3 @@ public ExtendsBigDecimal(String val) { } } - diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/MultipleBoundsContainer.java b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/MultipleBoundsContainer.java index 39682cf4b..4d113a040 100644 --- a/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/MultipleBoundsContainer.java +++ b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/MultipleBoundsContainer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2024 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 @@ -19,16 +19,16 @@ import java.util.List; import java.util.Queue; -public class MultipleBoundsContainer implements TypeContainer> { - protected List instance; +public class MultipleBoundsContainer & Queue> implements TypeContainer> { + protected List instance; @Override - public List getInstance() { + public List getInstance() { return instance; } @Override - public void setInstance(List instance) { + public void setInstance(List instance) { this.instance = instance; } } diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/jsonp/JsonpTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/jsonp/JsonpTest.java index 731c9f709..3ea6779b3 100644 --- a/src/test/java/org/eclipse/yasson/defaultmapping/jsonp/JsonpTest.java +++ b/src/test/java/org/eclipse/yasson/defaultmapping/jsonp/JsonpTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024 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 @@ -22,19 +22,18 @@ import jakarta.json.JsonObjectBuilder; import jakarta.json.JsonString; import jakarta.json.JsonValue; -import jakarta.json.bind.Jsonb; -import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import jakarta.json.spi.JsonProvider; import org.eclipse.yasson.defaultmapping.jsonp.model.JsonpPojo; import org.junit.jupiter.api.Test; import static org.eclipse.yasson.Jsonbs.defaultJsonb; +import static org.eclipse.yasson.Jsonbs.testWithJsonbBuilderCreate; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; /** * Default mapping JSONP integration tests. @@ -114,45 +113,45 @@ public void testMarshallJsonString() { @Test public void testJsonPojo() { - JsonbConfig config = new JsonbConfig(); -// config.withFormatting(true); - Jsonb jsonb = JsonbBuilder.create(config); + // config.withFormatting(true); + testWithJsonbBuilderCreate(new JsonbConfig(), jsonb -> { + JsonpPojo pojo = new JsonpPojo(); + final JsonObjectBuilder obj1builder = JsonProvider.provider().createObjectBuilder(); + obj1builder.add("strVal", "string value"); + obj1builder.add("numVal", 2.0d); + obj1builder.addNull("nullVal"); + obj1builder.add("boolVal", Boolean.TRUE); - JsonpPojo pojo = new JsonpPojo(); - final JsonObjectBuilder obj1builder = JsonProvider.provider().createObjectBuilder(); - obj1builder.add("strVal", "string value"); - obj1builder.add("numVal", 2.0d); - obj1builder.addNull("nullVal"); - obj1builder.add("boolVal", Boolean.TRUE); - - final JsonObjectBuilder obj2Builder = JsonProvider.provider().createObjectBuilder(); - obj2Builder.add("innerStr", "string val"); - obj2Builder.add("innerNum", 11.1d); - final JsonObject obj2 = obj2Builder.build(); + final JsonObjectBuilder obj2Builder = JsonProvider.provider().createObjectBuilder(); + obj2Builder.add("innerStr", "string val"); + obj2Builder.add("innerNum", 11.1d); + final JsonObject obj2 = obj2Builder.build(); - JsonArrayBuilder array1Builder = JsonProvider.provider().createArrayBuilder(); - array1Builder.addNull().add(false).add(11L).add(BigDecimal.TEN).add("array STR value").add(obj2); - JsonArray jsonArray1 = array1Builder.build(); + JsonArrayBuilder array1Builder = JsonProvider.provider().createArrayBuilder(); + array1Builder.addNull().add(false).add(11L).add(BigDecimal.TEN).add("array STR value").add(obj2); + JsonArray jsonArray1 = array1Builder.build(); - obj1builder.add("innerJsonObject", obj2); - obj1builder.add("innerArrayObject", jsonArray1); + obj1builder.add("innerJsonObject", obj2); + obj1builder.add("innerArrayObject", jsonArray1); - final JsonObject obj1 = obj1builder.build(); - pojo.jsonObject = obj1; + final JsonObject obj1 = obj1builder.build(); + pojo.jsonObject = obj1; - JsonArrayBuilder arrayBuilder = JsonProvider.provider().createArrayBuilder(); - arrayBuilder.add(obj1).add(true).add(obj2).add(101.0d).add(BigDecimal.TEN); - pojo.jsonArray = arrayBuilder.build(); + JsonArrayBuilder arrayBuilder = JsonProvider.provider().createArrayBuilder(); + arrayBuilder.add(obj1).add(true).add(obj2).add(101.0d).add(BigDecimal.TEN); + pojo.jsonArray = arrayBuilder.build(); - String expected = "{\"jsonArray\":[{\"strVal\":\"string value\",\"numVal\":2.0,\"nullVal\":null,\"boolVal\":true,\"innerJsonObject\":{\"innerStr\":\"string val\",\"innerNum\":11.1},\"innerArrayObject\":[null,false,11,10,\"array STR value\",{\"innerStr\":\"string val\",\"innerNum\":11.1}]},true,{\"innerStr\":\"string val\",\"innerNum\":11.1},101.0,10],\"jsonObject\":{\"strVal\":\"string value\",\"numVal\":2.0,\"nullVal\":null,\"boolVal\":true,\"innerJsonObject\":{\"innerStr\":\"string val\",\"innerNum\":11.1},\"innerArrayObject\":[null,false,11,10,\"array STR value\",{\"innerStr\":\"string val\",\"innerNum\":11.1}]}}"; - final String actual = jsonb.toJson(pojo); - assertEquals(expected, actual); + String expected = + "{\"jsonArray\":[{\"strVal\":\"string value\",\"numVal\":2.0,\"nullVal\":null,\"boolVal\":true,\"innerJsonObject\":{\"innerStr\":\"string val\",\"innerNum\":11.1},\"innerArrayObject\":[null,false,11,10,\"array STR value\",{\"innerStr\":\"string val\",\"innerNum\":11.1}]},true,{\"innerStr\":\"string val\",\"innerNum\":11.1},101.0,10],\"jsonObject\":{\"strVal\":\"string value\",\"numVal\":2.0,\"nullVal\":null,\"boolVal\":true,\"innerJsonObject\":{\"innerStr\":\"string val\",\"innerNum\":11.1},\"innerArrayObject\":[null,false,11,10,\"array STR value\",{\"innerStr\":\"string val\",\"innerNum\":11.1}]}}"; + final String actual = jsonb.toJson(pojo); + assertEquals(expected, actual); - JsonpPojo result = jsonb.fromJson(expected, JsonpPojo.class); - assertEquals(pojo.jsonObject, result.jsonObject); - assertEquals(pojo.jsonArray, result.jsonArray); + JsonpPojo result = jsonb.fromJson(expected, JsonpPojo.class); + assertEquals(pojo.jsonObject, result.jsonObject); + assertEquals(pojo.jsonArray, result.jsonArray); + }); } @Test @@ -210,7 +209,7 @@ public void testJsonValueString() { assertEquals("{\"jsonValue\":\"abc\"}", json); JsonValueWrapper result = defaultJsonb.fromJson("{\"jsonValue\":\"def\"}", JsonValueWrapper.class); - assertTrue(result.jsonValue instanceof JsonString); + assertInstanceOf(JsonString.class, result.jsonValue); assertEquals("def", ((JsonString)result.jsonValue).getString()); } @@ -226,7 +225,7 @@ public void testJsonValueAsObject() { assertEquals(expected, json); JsonValueWrapper result = defaultJsonb.fromJson(expected, JsonValueWrapper.class); - assertTrue(result.jsonValue instanceof JsonObject); + assertInstanceOf(JsonObject.class, result.jsonValue); JsonObject jsonObject = (JsonObject) result.jsonValue; assertEquals("val1", jsonObject.getString("prop1")); assertEquals("innerVal1", jsonObject.getJsonObject("innerObj1").getString("inner1")); @@ -241,7 +240,7 @@ public void testJsonValueAsArray() { assertEquals(expected, json); JsonValueWrapper result = defaultJsonb.fromJson(expected, JsonValueWrapper.class); - assertTrue(result.jsonValue instanceof JsonArray); + assertInstanceOf(JsonArray.class, result.jsonValue); JsonArray resultArray = (JsonArray) result.jsonValue; assertEquals(1, resultArray.getInt(0)); assertEquals(2, resultArray.getInt(1)); diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/specific/OptionalTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/specific/OptionalTest.java index 8bb2444a9..584a43896 100644 --- a/src/test/java/org/eclipse/yasson/defaultmapping/specific/OptionalTest.java +++ b/src/test/java/org/eclipse/yasson/defaultmapping/specific/OptionalTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024 Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2019, 2020 Payara Foundation and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the @@ -26,10 +26,7 @@ import org.eclipse.yasson.defaultmapping.specific.model.OptionalWrapper; import org.eclipse.yasson.defaultmapping.specific.model.NotMatchingGettersAndSetters; import org.eclipse.yasson.defaultmapping.specific.model.Street; -import org.eclipse.yasson.internal.JsonBindingBuilder; -import jakarta.json.bind.Jsonb; -import jakarta.json.bind.JsonbBuilder; import java.util.*; /** @@ -44,7 +41,7 @@ public class OptionalTest { public void testOptionalString() { assertEquals("{\"value\":\"abc\"}", bindingJsonb.toJson(new ScalarValueWrapper<>(Optional.of("abc")))); - ScalarValueWrapper result = bindingJsonb.fromJson("{\"value\":\"abc\"}", new TestTypeToken>() {}.getType()); + ScalarValueWrapper> result = bindingJsonb.fromJson("{\"value\":\"abc\"}", new TestTypeToken>>() {}.getType()); assertEquals(Optional.of("abc"), result.getValue()); } @@ -100,7 +97,7 @@ public void testMarshallOptionalIntArray() { @Test public void testMarshallOptionalArray() { - final Optional[] array = {Optional.of(new Customer(1, "Cust1")), Optional.of(new Customer(2, "Cust2")), Optional.empty()}; + final Optional[] array = {Optional.of(new Customer(1, "Cust1")), Optional.of(new Customer(2, "Cust2")), Optional.empty()}; assertEquals("[{\"id\":1,\"name\":\"Cust1\"},{\"id\":2,\"name\":\"Cust2\"},null]", bindingJsonb.toJson(array)); } diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/specific/UnmarshallingUnsupportedTypesTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/specific/UnmarshallingUnsupportedTypesTest.java index 4963b12e9..e86babcfe 100644 --- a/src/test/java/org/eclipse/yasson/defaultmapping/specific/UnmarshallingUnsupportedTypesTest.java +++ b/src/test/java/org/eclipse/yasson/defaultmapping/specific/UnmarshallingUnsupportedTypesTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024 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 @@ -23,8 +23,6 @@ import java.util.OptionalInt; import java.util.OptionalLong; -import jakarta.json.bind.Jsonb; -import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.JsonbException; @@ -37,10 +35,12 @@ import org.junit.jupiter.api.Test; import static org.eclipse.yasson.Jsonbs.defaultJsonb; +import static org.eclipse.yasson.Jsonbs.testWithJsonbBuilderCreate; import static org.eclipse.yasson.YassonConfig.FAIL_ON_UNKNOWN_PROPERTIES; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -120,7 +120,7 @@ public void testMissingFieldDefault() { String json = "{\"nestedPojo\":{\"integerValue\":10,\"missingField\":5},\"optionalLong\":11}"; SupportedTypes result = defaultJsonb.fromJson(json, SupportedTypes.class); assertEquals(Integer.valueOf(10), result.getNestedPojo().getIntegerValue()); - assertEquals(11, result.getOptionalLong().getAsLong()); + assertEquals(11, result.getOptionalLong().orElseThrow(RuntimeException::new)); } @Test() @@ -128,16 +128,18 @@ public void testMissingFieldDefaultNull() { String json = "{\"nestedPojo\":{\"integerValue\":10,\"missingField\":null},\"optionalLong\":11}"; SupportedTypes result = defaultJsonb.fromJson(json, SupportedTypes.class); assertEquals(Integer.valueOf(10), result.getNestedPojo().getIntegerValue()); - assertEquals(11, result.getOptionalLong().getAsLong()); + assertEquals(11, result.getOptionalLong().orElseThrow(RuntimeException::new)); } @Test public void testMissingFieldIgnored() { - assertThrows(JsonbException.class, () -> { - Jsonb defaultConfig = JsonbBuilder.create(new JsonbConfig().setProperty(FAIL_ON_UNKNOWN_PROPERTIES, true)); - String json = "{\"nestedPojo\":{\"integerValue\":10,\"missingField\":5},\"optionalLong\":11}"; - SupportedTypes result = defaultConfig.fromJson(json, SupportedTypes.class); - }); + RuntimeException runtimeException + = assertThrows(RuntimeException.class, () -> testWithJsonbBuilderCreate(new JsonbConfig().setProperty(FAIL_ON_UNKNOWN_PROPERTIES, true), jsonb -> { + String json = "{\"nestedPojo\":{\"integerValue\":10,\"missingField\":5},\"optionalLong\":11}"; + SupportedTypes result = jsonb.fromJson(json, SupportedTypes.class); //need this variable so the exception is thrown + })); + + assertInstanceOf(JsonbException.class, runtimeException.getCause()); } @Test diff --git a/src/test/java/org/eclipse/yasson/documented/DocumentationExampleTest.java b/src/test/java/org/eclipse/yasson/documented/DocumentationExampleTest.java index d49e4a07a..1fcdb8626 100644 --- a/src/test/java/org/eclipse/yasson/documented/DocumentationExampleTest.java +++ b/src/test/java/org/eclipse/yasson/documented/DocumentationExampleTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024 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 @@ -12,19 +12,24 @@ package org.eclipse.yasson.documented; -import org.junit.jupiter.api.*; -import static org.junit.jupiter.api.Assertions.*; -import static org.eclipse.yasson.Jsonbs.*; +import static org.eclipse.yasson.Jsonbs.defaultJsonb; +import static org.eclipse.yasson.Jsonbs.formattingJsonb; +import static org.eclipse.yasson.Jsonbs.nullableJsonb; +import static org.eclipse.yasson.Jsonbs.testWithJsonbBuilderCreate; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; import java.lang.reflect.Type; import java.math.BigDecimal; +import java.text.DecimalFormat; import java.time.LocalDate; import java.util.ArrayList; import java.util.List; import java.util.Map; -import jakarta.json.bind.Jsonb; -import jakarta.json.bind.JsonbBuilder; +import org.junit.jupiter.api.Test; + import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.adapter.JsonbAdapter; import jakarta.json.bind.annotation.JsonbCreator; @@ -41,525 +46,548 @@ import jakarta.json.stream.JsonParser; /** - * Contains tests from http://json-b.net/docs/user-guide.html + * Contains tests from user-guide */ public class DocumentationExampleTest { - public static class Dog { - public String name; - public int age; - public boolean bitable; - } - - @Test - public void testMappingExample() { - // Create a dog instance - Dog dog = new Dog(); - dog.name = "Falco"; - dog.age = 4; - dog.bitable = false; - - // Create Jsonb and serialize - String result = defaultJsonb.toJson(dog); - assertEquals("{\"age\":4,\"bitable\":false,\"name\":\"Falco\"}", result); - - // Deserialize back - dog = defaultJsonb.fromJson("{\"name\":\"Falco\",\"age\":4,\"bites\":false}", Dog.class); - assertEquals("Falco", dog.name); - assertEquals(4, dog.age); - assertEquals(false, dog.bitable); - } - - @Test - @SuppressWarnings({ "rawtypes", "unchecked" }) - public void testMappingCollection() { - Dog falco = new Dog(); - falco.name = "Falco"; - falco.age = 4; - Dog cassidy = new Dog(); - cassidy.name = "Cassidy"; - cassidy.age = 5; - - // List of dogs - List dogs = new ArrayList(); - dogs.add(falco); - dogs.add(cassidy); - - // Create Jsonb and serialize - String result = defaultJsonb.toJson(dogs); - assertEquals( - "[{\"age\":4,\"bitable\":false,\"name\":\"Falco\"},{\"age\":5,\"bitable\":false,\"name\":\"Cassidy\"}]", - result); - - // We can also deserialize back into a raw collection, but since there is no way - // to infer a type here, - // the result will be a list of java.util.Map instances with string keys. - dogs = defaultJsonb.fromJson(result, ArrayList.class); - assertEquals(2, dogs.size()); - assertEquals("Falco", ((Map) dogs.get(0)).get("name")); - assertEquals("Cassidy", ((Map) dogs.get(1)).get("name")); - // assertEquals(4, ((Map) dogs.get(0)).get("age")); // TODO should these - // actually be BigDecimals? - // assertEquals(5, ((Map) dogs.get(1)).get("age")); - } - - @SuppressWarnings("serial") - @Test - public void testMappingGenericCollection() { - Dog falco = new Dog(); - falco.name = "Falco"; - falco.age = 4; - Dog cassidy = new Dog(); - cassidy.name = "Cassidy"; - cassidy.age = 5; - - // List of dogs - List dogs = new ArrayList<>(); - dogs.add(falco); - dogs.add(cassidy); - - // Create Jsonb and serialize - String result = defaultJsonb.toJson(dogs); - assertEquals( - "[{\"age\":4,\"bitable\":false,\"name\":\"Falco\"},{\"age\":5,\"bitable\":false,\"name\":\"Cassidy\"}]", - result); - - // Deserialize back - dogs = defaultJsonb.fromJson(result, new ArrayList() { - }.getClass().getGenericSuperclass()); - assertEquals(2, dogs.size()); - assertEquals("Falco", dogs.get(0).name); - assertEquals("Cassidy", dogs.get(1).name); - } - - @Test - public void testFormattedOutput() { - Dog pojo = new Dog(); - pojo.name = "Falco"; - pojo.age = 4; - - // Use it! - String result = formattingJsonb.toJson(pojo); - assertEquals("{\n" + - " \"age\": 4,\n" + - " \"bitable\": false,\n" + - " \"name\": \"Falco\"\n" + - "}", result); - } - - public static class Person1 { - @JsonbProperty("person-name") - public String name; - public String profession; - } - - @Test - public void testChangingPropertyNames1() { - Person1 p = new Person1(); - p.name = "Jason Bourne"; - p.profession = "Super Agent"; - - String result = formattingJsonb.toJson(p); - assertEquals("{\n" + - " \"person-name\": \"Jason Bourne\",\n" + - " \"profession\": \"Super Agent\"\n" + - "}", result); - } - - public class Person2 { - private String name; - private String profession; - - @JsonbProperty("person-name") - public String getName() { - return name; - } - - public String getProfession() { - return profession; - } - - // public setters ... - public void setName(String name) { - this.name = name; - } - public void setProfession(String profession) { - this.profession = profession; - } - } - - @Test - public void testChangingPropertyNames2() { - Person2 p = new Person2(); - p.name = "Jason Bourne"; - p.profession = "Super Agent"; - - String result = formattingJsonb.toJson(p); - assertEquals("{\n" + - " \"person-name\": \"Jason Bourne\",\n" + - " \"profession\": \"Super Agent\"\n" + - "}", result); - } - - public static class Person3 { - private String name; - - @JsonbProperty("name-to-write") - public String getName() { - return name; - } - - @JsonbProperty("name-to-read") - public void setName(String name) { - this.name = name; - } - } - - @Test - public void testChangingPropertyNames3() { - Person3 p = new Person3(); - p.name = "Jason Bourne"; - String result = defaultJsonb.toJson(p); - assertEquals("{\"name-to-write\":\"Jason Bourne\"}", result); - - String json = "{\"name-to-read\":\"Jason Bourne\"}"; - Person3 after = defaultJsonb.fromJson(json, Person3.class); - assertEquals("Jason Bourne", after.name); - } - - public static class Person4 { // TODO: a non-static class results in an NPE - @JsonbTransient - private String name; - - private String profession; - - // public getters/setters ... - public String getName() { - return name; - } - public void setName(String name) { - this.name = name; - } - public String getProfession() { - return this.profession; - } - public void setProfession(String profession) { - this.profession = profession; - } - } - - @Test - public void testIgnoringProperties() { - Person4 p = new Person4(); - p.name = "Jason Bourne"; - p.profession = "Super Agent"; - String result = defaultJsonb.toJson(p); - assertEquals("{\"profession\":\"Super Agent\"}", result); - - String json = "{\"profession\":\"Super Agent\"}"; - Person4 after = defaultJsonb.fromJson(json, Person4.class); - assertEquals("Super Agent", after.profession); - assertNull(after.name); - } - - @JsonbNillable - public class Person5 { - private String name; - private String profession; - - // public getters/setters ... - public String getName() { - return name; - } - public void setName(String name) { - this.name = name; - } - public String getProfession() { - return profession; - } - public void setProfession(String profession) { - this.profession = profession; - } - } - - @Test - public void testNullHandling1() { - Person5 p = new Person5(); - String result = defaultJsonb.toJson(p); - assertEquals("{\"name\":null,\"profession\":null}", result); - } - - public class Person6 { - @JsonbProperty(nillable=true) - private String name; - - private String profession; - - // public getters/setters ... - public String getName() { - return name; - } - public void setName(String name) { - this.name = name; - } - public String getProfession() { - return profession; - } - public void setProfession(String profession) { - this.profession = profession; - } - } - - @Test - public void testNullHandling2() { - Person6 p = new Person6(); - String result = defaultJsonb.toJson(p); - assertEquals("{\"name\":null}", result); - } - - public static class Person { - public String name; - public String profession; - } - - @Test - public void testNullHandling3() { - Person p = new Person(); - String result = nullableJsonb.toJson(p); - assertEquals("{\"name\":null,\"profession\":null}", result); - } - - public static class Person8 { // TODO: obscure error here if non-static - public final String name; - public String profession; - - @JsonbCreator - public Person8(@JsonbProperty("name") String name) { - this.name = name; - } - } - - @Test - public void testCustomInstantiation() { - Person8 p = defaultJsonb.fromJson("{\"name\":\"Jason Bourne\"}", Person8.class); - assertEquals("Jason Bourne", p.name); - } - - public static class Person9 { - public String name; - - @JsonbDateFormat("dd.MM.yyyy") - public LocalDate birthDate; - - @JsonbNumberFormat("#0.00") - public BigDecimal salary; - } - - @Test - public void testDateNumberFormats1() { - Person9 p = new Person9(); - p.name = "Jason Bourne"; - p.birthDate = LocalDate.of(1999, 8, 7); - p.salary = new BigDecimal("123.45678"); - String json = defaultJsonb.toJson(p); - assertEquals("{\"birthDate\":\"07.08.1999\",\"name\":\"Jason Bourne\",\"salary\":\"123.46\"}", json); - - Person9 after = defaultJsonb.fromJson("{\"birthDate\":\"07.08.1999\",\"name\":\"Jason Bourne\",\"salary\":\"123.46\"}", Person9.class); - assertEquals(p.name, after.name); - assertEquals(p.birthDate, after.birthDate); - assertEquals(new BigDecimal("123.46"), after.salary); - } - - public static class Person10 { - public String name; - - public LocalDate birthDate; - - public BigDecimal salary; - } - - @Test - public void testDateNumberFormats2() { - Person10 p = new Person10(); - p.name = "Jason Bourne"; - p.birthDate = LocalDate.of(1999, 8, 7); - p.salary = new BigDecimal("123.45678"); - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()// - .withDateFormat("dd.MM.yyyy", null)); // TODO: why no withNumberFormat? - String json = jsonb.toJson(p); - assertEquals("{\"birthDate\":\"07.08.1999\",\"name\":\"Jason Bourne\",\"salary\":123.45678}", json); - - Person9 after = jsonb.fromJson("{\"birthDate\":\"07.08.1999\",\"name\":\"Jason Bourne\",\"salary\":123.45678}", Person9.class); - assertEquals(p.name, after.name); - assertEquals(p.birthDate, after.birthDate); - assertEquals(p.salary, after.salary); - } - - public static class Customer { - private int id; - private String name; - private String organization; - private String position; - - public int getId() { - return id; - } - public void setId(int id) { - this.id = id; - } - public String getName() { - return name; - } - public void setName(String name) { - this.name = name; - } - public String getOrganization() { - return organization; - } - public void setOrganization(String organization) { - this.organization = organization; - } - public String getPosition() { - return position; - } - public void setPosition(String position) { - this.position = position; - } - } - - public static class CustomerAnnotated { - @JsonbProperty("customer_id") - private int id; - - @JsonbProperty("customer_name") - private String name; - - public int getId() { - return id; - } - public void setId(int id) { - this.id = id; - } - public String getName() { - return name; - } - public void setName(String name) { - this.name = name; - } - } - - public static class CustomerAdapter implements JsonbAdapter { - @Override - public CustomerAnnotated adaptToJson(Customer c) throws Exception { - CustomerAnnotated customer = new CustomerAnnotated(); - customer.setId(c.getId()); - customer.setName(c.getName()); - return customer; - } - - @Override - public Customer adaptFromJson(CustomerAnnotated adapted) throws Exception { - Customer customer = new Customer(); - customer.setId(adapted.getId()); - customer.setName(adapted.getName()); - return customer; - } - } - - @Test - public void testAdapters1() { - // Create customer - Customer customer = new Customer(); - - customer.setId(1); - customer.setName("Jason Bourne"); - customer.setOrganization("Super Agents"); - customer.setPosition("Super Agent"); - - // Serialize - String json = defaultJsonb.toJson(customer); - assertEquals("{\"id\":1,\"name\":\"Jason Bourne\",\"organization\":\"Super Agents\",\"position\":\"Super Agent\"}", json); - } - - @Test - public void testAdapters2() { - // Create custom configuration - JsonbConfig config = new JsonbConfig() - .withAdapters(new CustomerAdapter()); - - // Create Jsonb with custom configuration - Jsonb jsonb = JsonbBuilder.create(config); - - // Create customer - Customer customer = new Customer(); - - customer.setId(1); - customer.setName("Jason Bourne"); - customer.setOrganization("Super Agents"); - customer.setPosition("Super Agent"); - - // Serialize - String json = jsonb.toJson(customer); - assertEquals("{\"customer_id\":1,\"customer_name\":\"Jason Bourne\"}", json); - } - - public static class CustomerSerializer implements JsonbSerializer { - @Override - public void serialize(Customer customer, JsonGenerator generator, SerializationContext ctx) { - generator.writeStartObject(); - generator.write("customer_id", customer.getId()); - generator.write("customer_name", customer.getName()); - generator.writeEnd(); - } - } - - public static class CustomerDeserializer implements JsonbDeserializer { - @Override - public Customer deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { - Customer customer = new Customer(); - JsonParser.Event next; - - // Moving parser by hand looking for customer_id and customer_name properties - while ((next = parser.next()) != JsonParser.Event.END_OBJECT) { - if (next == JsonParser.Event.KEY_NAME) { - String jsonKeyName = parser.getString(); - - // Move to json value - parser.next(); - - if ("customer_id".equals(jsonKeyName)) { - customer.setId(parser.getInt()); - } else if ("customer_name".equals(jsonKeyName)) { - customer.setName(parser.getString()); - } - } - } - return customer; - } - } - - @Test - public void testSerializerDeserializer() { - // Create pojo - Customer customer = new Customer(); - customer.setId(1); - customer.setName("Freddie"); - customer.setOrganization("Super Agents"); - customer.setPosition("Super Agent"); - - // Also configurable with @JsonbSerializer / JsonbDeserializer on properties and class. - JsonbConfig config = new JsonbConfig() - .withSerializers(new CustomerSerializer()) - .withDeserializers(new CustomerDeserializer()); - - Jsonb jsonb = JsonbBuilder.create(config); - String json = jsonb.toJson(customer); - assertEquals("{\"customer_id\":1,\"customer_name\":\"Freddie\"}", json); - - Customer result = jsonb.fromJson(json, Customer.class); - assertEquals(customer.getId(), result.getId()); - assertEquals(customer.getName(), result.getName()); - assertNull(result.getOrganization()); - assertNull(result.getPosition()); - } + private static final DecimalFormat DEFAULT_PERSON_SALARY_DECIMAL_FORMAT = new DecimalFormat("#0.00"); + + public static class Dog { + public String name; + public int age; + public boolean bitable; + } + + @Test + public void testMappingExample() { + // Create a dog instance + Dog dog = new Dog(); + dog.name = "Falco"; + dog.age = 4; + dog.bitable = false; + + // Create Jsonb and serialize + String result = defaultJsonb.toJson(dog); + assertEquals("{\"age\":4,\"bitable\":false,\"name\":\"Falco\"}", result); + + // Deserialize back + dog = defaultJsonb.fromJson("{\"name\":\"Falco\",\"age\":4,\"bites\":false}", Dog.class); + assertEquals("Falco", dog.name); + assertEquals(4, dog.age); + assertFalse(dog.bitable); + } + + @Test + @SuppressWarnings({"rawtypes", "unchecked"}) + public void testMappingCollection() { + Dog falco = new Dog(); + falco.name = "Falco"; + falco.age = 4; + Dog cassidy = new Dog(); + cassidy.name = "Cassidy"; + cassidy.age = 5; + + // List of dogs + List dogs = new ArrayList(); + dogs.add(falco); + dogs.add(cassidy); + + // Create Jsonb and serialize + String result = defaultJsonb.toJson(dogs); + assertEquals( + "[{\"age\":4,\"bitable\":false,\"name\":\"Falco\"},{\"age\":5,\"bitable\":false,\"name\":\"Cassidy\"}]", + result); + + // We can also deserialize back into a raw collection, but since there is no way + // to infer a type here, + // the result will be a list of java.util.Map instances with string keys. + dogs = defaultJsonb.fromJson(result, ArrayList.class); + assertEquals(2, dogs.size()); + assertEquals("Falco", ((Map) dogs.get(0)).get("name")); + assertEquals("Cassidy", ((Map) dogs.get(1)).get("name")); + // assertEquals(4, ((Map) dogs.get(0)).get("age")); // TODO should these + // actually be BigDecimals? + // assertEquals(5, ((Map) dogs.get(1)).get("age")); + } + + @Test + public void testMappingGenericCollection() { + Dog falco = new Dog(); + falco.name = "Falco"; + falco.age = 4; + Dog cassidy = new Dog(); + cassidy.name = "Cassidy"; + cassidy.age = 5; + + // List of dogs + List dogs = new ArrayList<>(); + dogs.add(falco); + dogs.add(cassidy); + + // Create Jsonb and serialize + String result = defaultJsonb.toJson(dogs); + assertEquals( + "[{\"age\":4,\"bitable\":false,\"name\":\"Falco\"},{\"age\":5,\"bitable\":false,\"name\":\"Cassidy\"}]", + result); + + // Deserialize back + dogs = defaultJsonb.fromJson(result, new ArrayList() { + }.getClass().getGenericSuperclass()); + assertEquals(2, dogs.size()); + assertEquals("Falco", dogs.get(0).name); + assertEquals("Cassidy", dogs.get(1).name); + } + + @Test + public void testFormattedOutput() { + Dog pojo = new Dog(); + pojo.name = "Falco"; + pojo.age = 4; + + // Use it! + String result = formattingJsonb.toJson(pojo); + assertEquals("{\n" + + " \"age\": 4,\n" + + " \"bitable\": false,\n" + + " \"name\": \"Falco\"\n" + + "}", result); + } + + public static class Person1 { + @JsonbProperty("person-name") + public String name; + public String profession; + } + + @Test + public void testChangingPropertyNames1() { + Person1 p = new Person1(); + p.name = "Jason Bourne"; + p.profession = "Super Agent"; + + String result = formattingJsonb.toJson(p); + assertEquals("{\n" + + " \"person-name\": \"Jason Bourne\",\n" + + " \"profession\": \"Super Agent\"\n" + + "}", result); + } + + public static class Person2 { + private String name; + private String profession; + + @JsonbProperty("person-name") + public String getName() { + return name; + } + + public String getProfession() { + return profession; + } + + // public setters ... + public void setName(String name) { + this.name = name; + } + + public void setProfession(String profession) { + this.profession = profession; + } + } + + @Test + public void testChangingPropertyNames2() { + Person2 p = new Person2(); + p.name = "Jason Bourne"; + p.profession = "Super Agent"; + + String result = formattingJsonb.toJson(p); + assertEquals("{\n" + + " \"person-name\": \"Jason Bourne\",\n" + + " \"profession\": \"Super Agent\"\n" + + "}", result); + } + + public static class Person3 { + private String name; + + @JsonbProperty("name-to-write") + public String getName() { + return name; + } + + @JsonbProperty("name-to-read") + public void setName(String name) { + this.name = name; + } + } + + @Test + public void testChangingPropertyNames3() { + Person3 p = new Person3(); + p.name = "Jason Bourne"; + String result = defaultJsonb.toJson(p); + assertEquals("{\"name-to-write\":\"Jason Bourne\"}", result); + + String json = "{\"name-to-read\":\"Jason Bourne\"}"; + Person3 after = defaultJsonb.fromJson(json, Person3.class); + assertEquals("Jason Bourne", after.name); + } + + public static class Person4 { // TODO: a non-static class results in an NPE + @JsonbTransient + private String name; + + private String profession; + + // public getters/setters ... + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getProfession() { + return this.profession; + } + + public void setProfession(String profession) { + this.profession = profession; + } + } + + @Test + public void testIgnoringProperties() { + Person4 p = new Person4(); + p.name = "Jason Bourne"; + p.profession = "Super Agent"; + String result = defaultJsonb.toJson(p); + assertEquals("{\"profession\":\"Super Agent\"}", result); + + String json = "{\"profession\":\"Super Agent\"}"; + Person4 after = defaultJsonb.fromJson(json, Person4.class); + assertEquals("Super Agent", after.profession); + assertNull(after.name); + } + + @JsonbNillable + public static class Person5 { + private String name; + private String profession; + + // public getters/setters ... + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getProfession() { + return profession; + } + + public void setProfession(String profession) { + this.profession = profession; + } + } + + @Test + public void testNullHandling1() { + Person5 p = new Person5(); + String result = defaultJsonb.toJson(p); + assertEquals("{\"name\":null,\"profession\":null}", result); + } + + public static class Person6 { + @SuppressWarnings("deprecation") + @JsonbProperty(nillable = true) + private String name; + + private String profession; + + // public getters/setters ... + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getProfession() { + return profession; + } + + public void setProfession(String profession) { + this.profession = profession; + } + } + + @Test + public void testNullHandling2() { + Person6 p = new Person6(); + String result = defaultJsonb.toJson(p); + assertEquals("{\"name\":null}", result); + } + + public static class Person { + public String name; + public String profession; + } + + @Test + public void testNullHandling3() { + Person p = new Person(); + String result = nullableJsonb.toJson(p); + assertEquals("{\"name\":null,\"profession\":null}", result); + } + + public static class Person8 { // TODO: obscure error here if non-static + public final String name; + public String profession; + + @JsonbCreator + public Person8(@JsonbProperty("name") String name) { + this.name = name; + } + } + + @Test + public void testCustomInstantiation() { + Person8 p = defaultJsonb.fromJson("{\"name\":\"Jason Bourne\"}", Person8.class); + assertEquals("Jason Bourne", p.name); + } + + public static class Person9 { + public String name; + + @JsonbDateFormat("dd.MM.yyyy") + public LocalDate birthDate; + + @JsonbNumberFormat("#0.00") + public BigDecimal salary; + } + + @Test + public void testDateNumberFormats1() { + Person9 p = new Person9(); + p.name = "Jason Bourne"; + p.birthDate = LocalDate.of(1999, 8, 7); + p.salary = new BigDecimal("123.45678"); + String json = defaultJsonb.toJson(p); + String expectedJson = "{\"birthDate\":\"07.08.1999\",\"name\":\"Jason Bourne\",\"salary\":\"" + DEFAULT_PERSON_SALARY_DECIMAL_FORMAT.format(p.salary) + + "\"}"; + assertEquals(expectedJson, json); + + Person9 after = defaultJsonb.fromJson(expectedJson, Person9.class); + assertEquals(p.name, after.name); + assertEquals(p.birthDate, after.birthDate); + assertEquals(new BigDecimal("123.46"), after.salary); + } + + public static class Person10 { + public String name; + + public LocalDate birthDate; + + public BigDecimal salary; + } + + @Test + public void testDateNumberFormats2() { + Person10 p = new Person10(); + p.name = "Jason Bourne"; + p.birthDate = LocalDate.of(1999, 8, 7); + p.salary = new BigDecimal("123.45678"); + testWithJsonbBuilderCreate(new JsonbConfig()// + .withDateFormat("dd.MM.yyyy", null), jsonb -> { // TODO: why no withNumberFormat? + String json = jsonb.toJson(p); + String expectedJson = "{\"birthDate\":\"07.08.1999\",\"name\":\"Jason Bourne\",\"salary\":123.45678}"; + assertEquals(expectedJson, json); + + Person10 after = jsonb.fromJson(expectedJson, Person10.class); + assertEquals(p.name, after.name); + assertEquals(p.birthDate, after.birthDate); + assertEquals(p.salary, after.salary); + }); + } + + public static class Customer { + private int id; + private String name; + private String organization; + private String position; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getOrganization() { + return organization; + } + + public void setOrganization(String organization) { + this.organization = organization; + } + + public String getPosition() { + return position; + } + + public void setPosition(String position) { + this.position = position; + } + } + + public static class CustomerAnnotated { + @JsonbProperty("customer_id") + private int id; + + @JsonbProperty("customer_name") + private String name; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + + public static class CustomerAdapter implements JsonbAdapter { + @Override + public CustomerAnnotated adaptToJson(Customer c) throws Exception { + CustomerAnnotated customer = new CustomerAnnotated(); + customer.setId(c.getId()); + customer.setName(c.getName()); + return customer; + } + + @Override + public Customer adaptFromJson(CustomerAnnotated adapted) throws Exception { + Customer customer = new Customer(); + customer.setId(adapted.getId()); + customer.setName(adapted.getName()); + return customer; + } + } + + @Test + public void testAdapters1() { + // Create customer + Customer customer = new Customer(); + + customer.setId(1); + customer.setName("Jason Bourne"); + customer.setOrganization("Super Agents"); + customer.setPosition("Super Agent"); + + // Serialize + String json = defaultJsonb.toJson(customer); + assertEquals("{\"id\":1,\"name\":\"Jason Bourne\",\"organization\":\"Super Agents\",\"position\":\"Super Agent\"}", json); + } + + @Test + public void testAdapters2() { + // Create custom configuration -> Create Jsonb with custom configuration + testWithJsonbBuilderCreate(new JsonbConfig() + .withAdapters(new CustomerAdapter()), jsonb -> { + + // Create customer + Customer customer = new Customer(); + + customer.setId(1); + customer.setName("Jason Bourne"); + customer.setOrganization("Super Agents"); + customer.setPosition("Super Agent"); + + // Serialize + String json = jsonb.toJson(customer); + assertEquals("{\"customer_id\":1,\"customer_name\":\"Jason Bourne\"}", json); + }); + } + + public static class CustomerSerializer implements JsonbSerializer { + @Override + public void serialize(Customer customer, JsonGenerator generator, SerializationContext ctx) { + generator.writeStartObject(); + generator.write("customer_id", customer.getId()); + generator.write("customer_name", customer.getName()); + generator.writeEnd(); + } + } + + public static class CustomerDeserializer implements JsonbDeserializer { + @Override + public Customer deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { + Customer customer = new Customer(); + JsonParser.Event next; + + // Moving parser by hand looking for customer_id and customer_name properties + while ((next = parser.next()) != JsonParser.Event.END_OBJECT) { + if (next == JsonParser.Event.KEY_NAME) { + String jsonKeyName = parser.getString(); + + // Move to json value + parser.next(); + + if ("customer_id".equals(jsonKeyName)) { + customer.setId(parser.getInt()); + } else if ("customer_name".equals(jsonKeyName)) { + customer.setName(parser.getString()); + } + } + } + return customer; + } + } + + @Test + public void testSerializerDeserializer() { + // Create pojo + Customer customer = new Customer(); + customer.setId(1); + customer.setName("Freddie"); + customer.setOrganization("Super Agents"); + customer.setPosition("Super Agent"); + + // Also configurable with @JsonbSerializer / JsonbDeserializer on properties and class. + testWithJsonbBuilderCreate(new JsonbConfig() + .withSerializers(new CustomerSerializer()) + .withDeserializers(new CustomerDeserializer()), jsonb -> { + String json = jsonb.toJson(customer); + assertEquals("{\"customer_id\":1,\"customer_name\":\"Freddie\"}", json); + + Customer result = jsonb.fromJson(json, Customer.class); + assertEquals(customer.getId(), result.getId()); + assertEquals(customer.getName(), result.getName()); + assertNull(result.getOrganization()); + assertNull(result.getPosition()); + }); + } } diff --git a/src/test/java/org/eclipse/yasson/internal/JsonBindingTest.java b/src/test/java/org/eclipse/yasson/internal/JsonBindingTest.java index dc6faf248..43a044c94 100644 --- a/src/test/java/org/eclipse/yasson/internal/JsonBindingTest.java +++ b/src/test/java/org/eclipse/yasson/internal/JsonBindingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020 IBM and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024 IBM 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 @@ -11,6 +11,7 @@ */ package org.eclipse.yasson.internal; +import static org.eclipse.yasson.Jsonbs.defaultJsonb; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; @@ -18,8 +19,8 @@ import java.lang.reflect.Field; import jakarta.json.bind.Jsonb; -import jakarta.json.bind.JsonbBuilder; +import org.eclipse.yasson.Jsonbs; import org.eclipse.yasson.YassonConfig; import org.eclipse.yasson.internal.model.ClassModel; import org.junit.jupiter.api.Test; @@ -31,26 +32,30 @@ public static class EagerParseClass { } @Test - public void testEagerInit() throws Exception { - Jsonb jsonb = JsonbBuilder.create(new YassonConfig() - .withEagerParsing(EagerParseClass.class)); - assertNotNull(getClassModel(jsonb, EagerParseClass.class)); - - EagerParseClass obj = new EagerParseClass(); - obj.foo = "foo"; - assertEquals("{\"foo\":\"foo\"}", jsonb.toJson(obj)); + public void testEagerInit() { + Jsonbs.testWithJsonbBuilderCreate(new YassonConfig() + .withEagerParsing(EagerParseClass.class), jsonb -> { + try { + assertNotNull(getClassModel(jsonb, EagerParseClass.class)); + } catch (Exception e) { + throw new RuntimeException(e); + } + + EagerParseClass obj = new EagerParseClass(); + obj.foo = "foo"; + assertEquals("{\"foo\":\"foo\"}", jsonb.toJson(obj)); + }); } @Test public void testNoEagerInit() throws Exception { - Jsonb jsonb = JsonbBuilder.create(); - assertNull(getClassModel(jsonb, EagerParseClass.class)); + assertNull(getClassModel(defaultJsonb, EagerParseClass.class)); EagerParseClass obj = new EagerParseClass(); obj.foo = "foo"; - assertEquals("{\"foo\":\"foo\"}", jsonb.toJson(obj)); + assertEquals("{\"foo\":\"foo\"}", defaultJsonb.toJson(obj)); - assertNotNull(getClassModel(jsonb, EagerParseClass.class)); + assertNotNull(getClassModel(defaultJsonb, EagerParseClass.class)); } private ClassModel getClassModel(Jsonb jsonb, Class clazz) throws Exception { diff --git a/src/test/java/org/eclipse/yasson/internal/cdi/CdiInjectionTest.java b/src/test/java/org/eclipse/yasson/internal/cdi/CdiInjectionTest.java index b17770c81..5d4cd57fe 100644 --- a/src/test/java/org/eclipse/yasson/internal/cdi/CdiInjectionTest.java +++ b/src/test/java/org/eclipse/yasson/internal/cdi/CdiInjectionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024 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 @@ -13,6 +13,8 @@ package org.eclipse.yasson.internal.cdi; import org.junit.jupiter.api.*; + +import static org.eclipse.yasson.Jsonbs.testWithJsonbBuilderCreate; import static org.junit.jupiter.api.Assertions.*; import org.eclipse.yasson.internal.components.JsonbComponentInstanceCreatorFactory; @@ -75,34 +77,30 @@ public void testInJndiEnvironment() throws NamingException { InitialContext context = new InitialContext(); context.bind(JsonbComponentInstanceCreatorFactory.BEAN_MANAGER_NAME, new JndiBeanManager()); - String result; try { - Jsonb jsonb = JsonbBuilder.create(); - result = jsonb.toJson(new AdaptedPojo()); + testWithJsonbBuilderCreate(jsonb -> + assertEquals("{\"adaptedValue1\":1111,\"adaptedValue2\":1001,\"adaptedValue3\":1010}", jsonb.toJson(new AdaptedPojo()))); } finally { context.unbind(JsonbComponentInstanceCreatorFactory.BEAN_MANAGER_NAME); } - assertEquals("{\"adaptedValue1\":1111,\"adaptedValue2\":1001,\"adaptedValue3\":1010}", result); - } + } @Test public void testNonCdiEnvironment() { - JsonbConfig config = new JsonbConfig(); + JsonbConfig config = new JsonbConfig() //allow only field with components that doesn't has cdi dependencies. - config.withPropertyVisibilityStrategy(new PropertyVisibilityStrategy() { - @Override - public boolean isVisible(Field field) { - return "adaptedValue3".equals(field.getName()); - } - - @Override - public boolean isVisible(Method method) { - return false; - } + .withPropertyVisibilityStrategy(new PropertyVisibilityStrategy() { + @Override + public boolean isVisible(Field field) { + return "adaptedValue3".equals(field.getName()); + } + + @Override + public boolean isVisible(Method method) { + return false; + } }); - Jsonb jsonb = JsonbBuilder.create(config); - final String result = jsonb.toJson(new AdaptedPojo()); - assertEquals("{\"adaptedValue3\":1010}", result); + testWithJsonbBuilderCreate(config, jsonb -> assertEquals("{\"adaptedValue3\":1010}", jsonb.toJson(new AdaptedPojo()))); } private CalledMethods getCalledMethods() { diff --git a/src/test/java/org/eclipse/yasson/internal/cdi/MockInjectionTarget.java b/src/test/java/org/eclipse/yasson/internal/cdi/MockInjectionTarget.java index a006739a2..2a92ddfc7 100644 --- a/src/test/java/org/eclipse/yasson/internal/cdi/MockInjectionTarget.java +++ b/src/test/java/org/eclipse/yasson/internal/cdi/MockInjectionTarget.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024 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 @@ -21,29 +21,30 @@ /** * For JNDI Bean Manager resolution testing purposes. */ -public class MockInjectionTarget implements InjectionTarget { +public class MockInjectionTarget implements InjectionTarget> { @Override - public void inject(JsonbAdapter instance, CreationalContext ctx) { + public void inject(JsonbAdapter instance, CreationalContext> ctx) { } @Override - public void postConstruct(JsonbAdapter instance) { + public void postConstruct(JsonbAdapter instance) { } @Override - public void preDestroy(JsonbAdapter instance) { + public void preDestroy(JsonbAdapter instance) { } @Override - public JsonbAdapter produce(CreationalContext ctx) { + public JsonbAdapter produce(CreationalContext> ctx) { + // for the test we only need this adapter; so it's OK to always return just this one return new NonCdiAdapter(); } @Override - public void dispose(JsonbAdapter instance) { + public void dispose(JsonbAdapter instance) { } diff --git a/src/test/java/org/eclipse/yasson/internal/cdi/MockInjectionTargetFactory.java b/src/test/java/org/eclipse/yasson/internal/cdi/MockInjectionTargetFactory.java index 3a6d8bc99..c65b3c641 100644 --- a/src/test/java/org/eclipse/yasson/internal/cdi/MockInjectionTargetFactory.java +++ b/src/test/java/org/eclipse/yasson/internal/cdi/MockInjectionTargetFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024 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 @@ -22,6 +22,8 @@ public class MockInjectionTargetFactory implements InjectionTargetFactory { @Override public InjectionTarget createInjectionTarget(Bean bean) { - return (InjectionTarget) new MockInjectionTarget(); + @SuppressWarnings("unchecked") + InjectionTarget injectionTarget = (InjectionTarget) new MockInjectionTarget(); + return injectionTarget; } } diff --git a/src/test/java/org/eclipse/yasson/internal/cdi/WeldManager.java b/src/test/java/org/eclipse/yasson/internal/cdi/WeldManager.java index 513a9e07f..f343f5bd0 100644 --- a/src/test/java/org/eclipse/yasson/internal/cdi/WeldManager.java +++ b/src/test/java/org/eclipse/yasson/internal/cdi/WeldManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024 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 @@ -31,7 +31,7 @@ public class WeldManager { private InitialContext initialContext; - public void startWeld(Class... scannedClasses) throws NamingException { + public void startWeld(Class... scannedClasses) throws NamingException { weld = new Weld().beanClasses(scannedClasses).disableDiscovery(); WeldContainer container = weld.initialize(); initialContext = new InitialContext(); diff --git a/src/test/java/org/eclipse/yasson/internal/model/customization/naming/PropertyNamingStrategyTest.java b/src/test/java/org/eclipse/yasson/internal/model/customization/naming/PropertyNamingStrategyTest.java index 33c0c60a7..2e872fa6e 100644 --- a/src/test/java/org/eclipse/yasson/internal/model/customization/naming/PropertyNamingStrategyTest.java +++ b/src/test/java/org/eclipse/yasson/internal/model/customization/naming/PropertyNamingStrategyTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024 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 @@ -13,10 +13,10 @@ package org.eclipse.yasson.internal.model.customization.naming; import org.junit.jupiter.api.*; + +import static org.eclipse.yasson.Jsonbs.testWithJsonbBuilderCreate; import static org.junit.jupiter.api.Assertions.*; -import jakarta.json.bind.Jsonb; -import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.config.PropertyNamingStrategy; @@ -32,7 +32,7 @@ public class PropertyNamingStrategyTest { private final NamingPojo pojo = new NamingPojo("abc", "def", "ghi"); @Test - public void testLowerCase() throws Exception { + public void testLowerCase() { PropertyNamingStrategy strategy = StrategiesProvider.getPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CASE_WITH_UNDERSCORES); assertEquals("camel_case_property", strategy.translateName("camelCaseProperty")); assertEquals("camelcase_property", strategy.translateName("CamelcaseProperty")); @@ -40,15 +40,17 @@ public void testLowerCase() throws Exception { assertEquals("_camel_case_property", strategy.translateName("_camelCaseProperty")); assertEquals("_camel_case_property", strategy.translateName("_CamelCaseProperty")); - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CASE_WITH_UNDERSCORES)); - String lowercaseUnderscoresJson = "{\"_starting_with_underscore_property\":\"def\",\"caps_underscore_property\":\"ghi\",\"upper_cased_property\":\"abc\"}"; - assertEquals(lowercaseUnderscoresJson, jsonb.toJson(pojo)); - NamingPojo result = jsonb.fromJson(lowercaseUnderscoresJson, NamingPojo.class); - assertResult(result); + testWithJsonbBuilderCreate(new JsonbConfig().withPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CASE_WITH_UNDERSCORES), jsonb -> { + String lowercaseUnderscoresJson = + "{\"_starting_with_underscore_property\":\"def\",\"caps_underscore_property\":\"ghi\",\"upper_cased_property\":\"abc\"}"; + assertEquals(lowercaseUnderscoresJson, jsonb.toJson(pojo)); + NamingPojo result = jsonb.fromJson(lowercaseUnderscoresJson, NamingPojo.class); + assertResult(result); + }); } @Test - public void testLowerDashes() throws Exception { + public void testLowerDashes() { PropertyNamingStrategy strategy = StrategiesProvider.getPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CASE_WITH_DASHES); assertEquals("camel-case-property", strategy.translateName("camelCaseProperty")); assertEquals("camelcase-property", strategy.translateName("CamelcaseProperty")); @@ -56,11 +58,13 @@ public void testLowerDashes() throws Exception { assertEquals("-camel-case-property", strategy.translateName("-camelCaseProperty")); assertEquals("-camel-case-property", strategy.translateName("-CamelCaseProperty")); - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CASE_WITH_DASHES)); - String lowercaseDashesJson = "{\"_starting-with-underscore-property\":\"def\",\"caps_underscore_property\":\"ghi\",\"upper-cased-property\":\"abc\"}"; - assertEquals(lowercaseDashesJson, jsonb.toJson(pojo)); - NamingPojo result = jsonb.fromJson(lowercaseDashesJson, NamingPojo.class); - assertResult(result); + testWithJsonbBuilderCreate(new JsonbConfig().withPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CASE_WITH_DASHES), jsonb -> { + String lowercaseDashesJson = + "{\"_starting-with-underscore-property\":\"def\",\"caps_underscore_property\":\"ghi\",\"upper-cased-property\":\"abc\"}"; + assertEquals(lowercaseDashesJson, jsonb.toJson(pojo)); + NamingPojo result = jsonb.fromJson(lowercaseDashesJson, NamingPojo.class); + assertResult(result); + }); } @Test @@ -69,11 +73,12 @@ public void testUpperCase() { assertEquals("UpperCamelCase", upperCaseStrategy.translateName("upperCamelCase")); assertEquals("UpperCamelCase", upperCaseStrategy.translateName("UpperCamelCase")); - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE)); - String upperCased = "{\"CAPS_UNDERSCORE_PROPERTY\":\"ghi\",\"UpperCasedProperty\":\"abc\",\"_startingWithUnderscoreProperty\":\"def\"}"; - assertEquals(upperCased, jsonb.toJson(pojo)); - NamingPojo result = jsonb.fromJson(upperCased, NamingPojo.class); - assertResult(result); + testWithJsonbBuilderCreate(new JsonbConfig().withPropertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE), jsonb -> { + String upperCased = "{\"CAPS_UNDERSCORE_PROPERTY\":\"ghi\",\"UpperCasedProperty\":\"abc\",\"_startingWithUnderscoreProperty\":\"def\"}"; + assertEquals(upperCased, jsonb.toJson(pojo)); + NamingPojo result = jsonb.fromJson(upperCased, NamingPojo.class); + assertResult(result); + }); } @Test @@ -82,39 +87,49 @@ public void testUpperCaseWithSpaces() { assertEquals("Upper Camel Case", upperCaseWithSpacesStrategy.translateName("upperCamelCase")); assertEquals("Upper Camel Case", upperCaseWithSpacesStrategy.translateName("UpperCamelCase")); - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE_WITH_SPACES)); - String upperCased = "{\"CAPS_UNDERSCORE_PROPERTY\":\"ghi\",\"Upper Cased Property\":\"abc\",\"_starting With Underscore Property\":\"def\"}"; - assertEquals(upperCased, jsonb.toJson(pojo)); - NamingPojo result = jsonb.fromJson(upperCased, NamingPojo.class); - assertResult(result); + testWithJsonbBuilderCreate(new JsonbConfig().withPropertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE_WITH_SPACES), jsonb -> { + String upperCased = + "{\"CAPS_UNDERSCORE_PROPERTY\":\"ghi\",\"Upper Cased Property\":\"abc\",\"_starting With Underscore Property\":\"def\"}"; + assertEquals(upperCased, jsonb.toJson(pojo)); + NamingPojo result = jsonb.fromJson(upperCased, NamingPojo.class); + assertResult(result); + }); } @Test public void testCaseInsensitive() { - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyNamingStrategy(PropertyNamingStrategy.CASE_INSENSITIVE)); - String upperCased = "{\"CAPS_UNDERSCORE_PROPERTY\":\"ghi\",\"_startingWithUnderscoreProperty\":\"def\",\"upperCasedProperty\":\"abc\"}"; - assertEquals(upperCased, jsonb.toJson(pojo)); - NamingPojo result = jsonb.fromJson("{\"caPS_unDERscore_prOPERty\":\"ghi\",\"_startingwithUndERSCorePrOPERTy\":\"def\",\"upPERCASedProPerty\":\"abc\"}", NamingPojo.class); - assertResult(result); + testWithJsonbBuilderCreate(new JsonbConfig().withPropertyNamingStrategy(PropertyNamingStrategy.CASE_INSENSITIVE), jsonb -> { + String upperCased = "{\"CAPS_UNDERSCORE_PROPERTY\":\"ghi\",\"_startingWithUnderscoreProperty\":\"def\",\"upperCasedProperty\":\"abc\"}"; + assertEquals(upperCased, jsonb.toJson(pojo)); + NamingPojo result = jsonb.fromJson( + "{\"caPS_unDERscore_prOPERty\":\"ghi\",\"_startingwithUndERSCorePrOPERTy\":\"def\",\"upPERCASedProPerty\":\"abc\"}", + NamingPojo.class); + assertResult(result); + }); } @Test public void testIdentityCaseSensitive() { - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyNamingStrategy(PropertyNamingStrategy.IDENTITY)); - NamingPojo result = jsonb.fromJson("{\"CAPS_UNDERSCORE_PROPERTY\":\"ghi\",\"_startingWithUnderscoreProperty\":\"def\",\"UPPERCASEDPROPERTY\":\"abc\"}", NamingPojo.class); - assertEquals("ghi", result.CAPS_UNDERSCORE_PROPERTY); - assertEquals("def", result._startingWithUnderscoreProperty); - assertNull(result.upperCasedProperty); + testWithJsonbBuilderCreate(new JsonbConfig().withPropertyNamingStrategy(PropertyNamingStrategy.IDENTITY), jsonb -> { + NamingPojo result = jsonb.fromJson( + "{\"CAPS_UNDERSCORE_PROPERTY\":\"ghi\",\"_startingWithUnderscoreProperty\":\"def\",\"UPPERCASEDPROPERTY\":\"abc\"}", + NamingPojo.class); + assertEquals("ghi", result.CAPS_UNDERSCORE_PROPERTY); + assertEquals("def", result._startingWithUnderscoreProperty); + assertNull(result.upperCasedProperty); + }); } @Test public void testCustom() { - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyNamingStrategy(propertyName -> propertyName + "_" + propertyName.toUpperCase())); - - String custom = "{\"CAPS_UNDERSCORE_PROPERTY_CAPS_UNDERSCORE_PROPERTY\":\"ghi\",\"_startingWithUnderscoreProperty__STARTINGWITHUNDERSCOREPROPERTY\":\"def\",\"upperCasedProperty_UPPERCASEDPROPERTY\":\"abc\"}"; - assertEquals(custom, jsonb.toJson(pojo)); - NamingPojo result = jsonb.fromJson(custom, NamingPojo.class); - assertResult(result); + testWithJsonbBuilderCreate(new JsonbConfig().withPropertyNamingStrategy(propertyName -> propertyName + "_" + propertyName.toUpperCase()), jsonb -> { + + String custom = + "{\"CAPS_UNDERSCORE_PROPERTY_CAPS_UNDERSCORE_PROPERTY\":\"ghi\",\"_startingWithUnderscoreProperty__STARTINGWITHUNDERSCOREPROPERTY\":\"def\",\"upperCasedProperty_UPPERCASEDPROPERTY\":\"abc\"}"; + assertEquals(custom, jsonb.toJson(pojo)); + NamingPojo result = jsonb.fromJson(custom, NamingPojo.class); + assertResult(result); + }); } private static void assertResult(NamingPojo result) { diff --git a/src/test/java/org/eclipse/yasson/jsonstructure/JsonGeneratorToStructureAdapterTest.java b/src/test/java/org/eclipse/yasson/jsonstructure/JsonGeneratorToStructureAdapterTest.java index f9771c642..f9df67f74 100644 --- a/src/test/java/org/eclipse/yasson/jsonstructure/JsonGeneratorToStructureAdapterTest.java +++ b/src/test/java/org/eclipse/yasson/jsonstructure/JsonGeneratorToStructureAdapterTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024 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 @@ -12,6 +12,7 @@ package org.eclipse.yasson.jsonstructure; +import org.eclipse.yasson.Jsonbs; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; @@ -24,7 +25,6 @@ import jakarta.json.JsonString; import jakarta.json.JsonStructure; import jakarta.json.JsonValue; -import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import java.math.BigDecimal; import java.util.ArrayList; @@ -63,10 +63,10 @@ public void testInnerObjects() { JsonObject result = (JsonObject) yassonJsonb.toJsonStructure(pojo, Pojo.class); assertEquals("String value", getString(result.get("stringProperty"))); JsonValue bigDecimalProperty = result.get("bigDecimalProperty"); - assertTrue(bigDecimalProperty instanceof JsonNumber); + assertInstanceOf(JsonNumber.class, bigDecimalProperty); assertEquals(BigDecimal.TEN, ((JsonNumber) bigDecimalProperty).bigDecimalValue()); JsonValue longProperty = result.get("longProperty"); - assertTrue(longProperty instanceof JsonNumber); + assertInstanceOf(JsonNumber.class, longProperty); assertEquals(10L, ((JsonNumber) longProperty).longValueExact()); JsonValue inner = result.get("inner"); @@ -139,15 +139,16 @@ public void testCustomJsonbSerializer() { pojo.setInner(new InnerPojo()); pojo.getInner().setInnerFirst("First value"); pojo.getInner().setInnerSecond("Second value"); - YassonJsonb jsonb = (YassonJsonb) JsonbBuilder.create(new JsonbConfig().withSerializers(new InnerPojoSerializer())); - JsonStructure result = jsonb.toJsonStructure(pojo); - assertEquals(JsonValue.ValueType.OBJECT, result.getValueType()); - assertEquals(JsonValue.ValueType.OBJECT, ((JsonObject) result).get("inner").getValueType()); - JsonObject inner = (JsonObject) ((JsonObject) result).get("inner"); - assertEquals(JsonValue.ValueType.STRING, inner.get("first").getValueType()); - assertEquals("First value", ((JsonString) inner.get("first")).getString()); - assertEquals(JsonValue.ValueType.STRING, inner.get("second").getValueType()); - assertEquals("Second value", ((JsonString) inner.get("second")).getString()); + Jsonbs.testWithJsonbBuilderCreate(new JsonbConfig().withSerializers(new InnerPojoSerializer()), jsonb -> { + JsonStructure result = ((YassonJsonb)jsonb).toJsonStructure(pojo); + assertEquals(JsonValue.ValueType.OBJECT, result.getValueType()); + assertEquals(JsonValue.ValueType.OBJECT, ((JsonObject) result).get("inner").getValueType()); + JsonObject inner = (JsonObject) ((JsonObject) result).get("inner"); + assertEquals(JsonValue.ValueType.STRING, inner.get("first").getValueType()); + assertEquals("First value", ((JsonString) inner.get("first")).getString()); + assertEquals(JsonValue.ValueType.STRING, inner.get("second").getValueType()); + assertEquals("Second value", ((JsonString) inner.get("second")).getString()); + }); } private static String getString(JsonValue value) { diff --git a/src/test/java/org/eclipse/yasson/jsonstructure/JsonStructureToParserAdapterTest.java b/src/test/java/org/eclipse/yasson/jsonstructure/JsonStructureToParserAdapterTest.java index f6d042acd..4d99802ec 100644 --- a/src/test/java/org/eclipse/yasson/jsonstructure/JsonStructureToParserAdapterTest.java +++ b/src/test/java/org/eclipse/yasson/jsonstructure/JsonStructureToParserAdapterTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024 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 @@ -12,30 +12,64 @@ package org.eclipse.yasson.jsonstructure; +import org.eclipse.yasson.internal.jsonstructure.JsonStructureToParserAdapter; +import org.hamcrest.Matcher; import org.junit.jupiter.api.*; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; import org.eclipse.yasson.TestTypeToken; import org.eclipse.yasson.YassonJsonb; +import org.junit.jupiter.api.function.Executable; +import jakarta.json.Json; import jakarta.json.JsonArray; import jakarta.json.JsonArrayBuilder; import jakarta.json.JsonObject; import jakarta.json.JsonObjectBuilder; -import jakarta.json.bind.JsonbBuilder; +import jakarta.json.JsonString; +import jakarta.json.JsonValue; import jakarta.json.bind.JsonbConfig; import jakarta.json.spi.JsonProvider; +import jakarta.json.stream.JsonParser; +import jakarta.json.stream.JsonParserFactory; + +import java.io.StringReader; import java.math.BigDecimal; +import java.math.BigInteger; import java.util.ArrayList; +import java.util.EnumSet; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.function.Consumer; +import java.util.stream.Collector; +import java.util.stream.Collectors; public class JsonStructureToParserAdapterTest { - private final JsonProvider jsonProvider = JsonProvider.provider(); + private static final EnumSet GET_STRING_EVENT_ENUM_SET = + EnumSet.of(JsonParser.Event.KEY_NAME, JsonParser.Event.VALUE_STRING, JsonParser.Event.VALUE_NUMBER); + + private static final EnumSet NOT_GET_VALUE_EVENT_ENUM_SET = EnumSet.of(JsonParser.Event.END_OBJECT, JsonParser.Event.END_ARRAY); + + private static final Collector, ?, ArrayList> MAP_TO_LIST_COLLECTOR = Collector.of(ArrayList::new, + (list, entry) -> { + list.add(entry.getKey()); + list.add(entry.getValue().toString()); + }, + (left, right) -> { + left.addAll(right); + return left; + }, + Collector.Characteristics.IDENTITY_FINISH); + + private static final JsonProvider jsonProvider = JsonProvider.provider(); @Test - public void testBasicJsonObject() { + void testBasicJsonObject() { JsonObjectBuilder objectBuilder = jsonProvider.createObjectBuilder(); objectBuilder.add("stringProperty", "value 1"); objectBuilder.add("bigDecimalProperty", new BigDecimal("1.1")); @@ -48,7 +82,7 @@ public void testBasicJsonObject() { } @Test - public void testNullValues() { + void testNullValues() { JsonObjectBuilder objectBuilder = jsonProvider.createObjectBuilder(); objectBuilder.addNull("stringProperty"); objectBuilder.addNull("bigDecimalProperty"); @@ -61,7 +95,7 @@ public void testNullValues() { } @Test - public void testInnerJsonObjectWrappedWithProperties() { + void testInnerJsonObjectWrappedWithProperties() { JsonObjectBuilder innerBuilder = jsonProvider.createObjectBuilder(); innerBuilder.add("innerFirst", "Inner value 1"); innerBuilder.add("innerSecond", "Inner value 2"); @@ -83,7 +117,7 @@ public void testInnerJsonObjectWrappedWithProperties() { } @Test - public void testInnerJsonObjectAtEndProperty() { + void testInnerJsonObjectAtEndProperty() { JsonObjectBuilder innerBuilder = jsonProvider.createObjectBuilder(); innerBuilder.add("innerFirst", "Inner value 1"); innerBuilder.add("innerSecond", "Inner value 2"); @@ -107,7 +141,7 @@ public void testInnerJsonObjectAtEndProperty() { } @Test - public void testEmptyJsonObject() { + void testEmptyJsonObject() { JsonObjectBuilder objectBuilder = jsonProvider.createObjectBuilder(); JsonObject jsonObject = objectBuilder.build(); Pojo result = yassonJsonb.fromJsonStructure(jsonObject, Pojo.class); @@ -117,7 +151,7 @@ public void testEmptyJsonObject() { } @Test - public void testEmptyInnerJsonObject() { + void testEmptyInnerJsonObject() { JsonObjectBuilder objectBuilder = jsonProvider.createObjectBuilder(); JsonObjectBuilder innerBuilder = jsonProvider.createObjectBuilder(); @@ -138,11 +172,11 @@ public void testEmptyInnerJsonObject() { } @Test - public void testSimpleArray() { + void testSimpleArray() { JsonArrayBuilder arrayBuilder = jsonProvider.createArrayBuilder(); arrayBuilder.add(BigDecimal.TEN).add("String value").addNull(); JsonArray jsonArray = arrayBuilder.build(); - List result = yassonJsonb.fromJsonStructure(jsonArray, ArrayList.class); + List result = yassonJsonb.fromJsonStructure(jsonArray, ArrayList.class); assertEquals(3, result.size()); assertEquals(BigDecimal.TEN, result.get(0)); assertEquals("String value", result.get(1)); @@ -150,7 +184,7 @@ public void testSimpleArray() { } @Test - public void testArraysInsideObject() { + void testArraysInsideObject() { JsonArrayBuilder bigDecBuilder = jsonProvider.createArrayBuilder(); JsonArrayBuilder strBuilder = jsonProvider.createArrayBuilder(); JsonArrayBuilder blnBuilder = jsonProvider.createArrayBuilder(); @@ -173,7 +207,7 @@ public void testArraysInsideObject() { } @Test - public void testNestedArrays() { + void testNestedArrays() { JsonArrayBuilder arrayBuilder = jsonProvider.createArrayBuilder(); JsonArrayBuilder innerArrBuilder = jsonProvider.createArrayBuilder(); innerArrBuilder.add("first").add("second"); @@ -182,18 +216,19 @@ public void testNestedArrays() { JsonArray jsonArray = arrayBuilder.build(); - ArrayList result = yassonJsonb.fromJsonStructure(jsonArray, ArrayList.class); + ArrayList result = yassonJsonb.fromJsonStructure(jsonArray, ArrayList.class); assertEquals(2, result.size()); assertEquals(BigDecimal.TEN, result.get(0)); - assertTrue(result.get(1) instanceof List); - List inner = (List) result.get(1); + assertInstanceOf(List.class, result.get(1)); + @SuppressWarnings("unchecked") + List inner = (List) result.get(1); assertEquals(2, inner.size()); assertEquals("first", inner.get(0)); assertEquals("second", inner.get(1)); } @Test - public void testObjectsNestedInArrays() { + void testObjectsNestedInArrays() { JsonObjectBuilder objectBuilder = jsonProvider.createObjectBuilder(); objectBuilder.add("stringProperty", "value 1"); objectBuilder.add("bigDecimalProperty", new BigDecimal("1.1")); @@ -209,7 +244,7 @@ public void testObjectsNestedInArrays() { JsonArray rootArray = arrayBuilder.build(); List result = yassonJsonb.fromJsonStructure(rootArray, new TestTypeToken>(){}.getType()); - assertTrue(result.get(0) instanceof Pojo); + assertInstanceOf(Pojo.class, result.get(0)); Pojo pojo = (Pojo) result.get(0); assertNotNull(pojo); assertEquals("value 1", pojo.getStringProperty()); @@ -221,7 +256,7 @@ public void testObjectsNestedInArrays() { } @Test - public void testObjectsNestedInArraysRaw() { + void testObjectsNestedInArraysRaw() { JsonObjectBuilder objectBuilder = jsonProvider.createObjectBuilder(); objectBuilder.add("stringProperty", "value 1"); objectBuilder.add("bigDecimalProperty", new BigDecimal("1.1")); @@ -241,14 +276,16 @@ public void testObjectsNestedInArraysRaw() { List result = yassonJsonb.fromJsonStructure(rootArray, new TestTypeToken>(){}.getType()); assertEquals(new BigDecimal("10"), result.get(0)); - assertTrue(result.get(1) instanceof Map); - Map pojo = (Map) result.get(1); + assertInstanceOf(Map.class, result.get(1)); + @SuppressWarnings("unchecked") + Map pojo = (Map) result.get(1); assertNotNull(pojo); assertEquals("value 1", pojo.get("stringProperty")); assertEquals(new BigDecimal("1.1"), pojo.get("bigDecimalProperty")); assertEquals(new BigDecimal(10), pojo.get("longProperty")); - assertTrue(pojo.get("strings") instanceof List); - List strings = (List) pojo.get("strings"); + assertInstanceOf(List.class, pojo.get("strings")); + @SuppressWarnings("unchecked") + List strings = (List) pojo.get("strings"); assertNotNull(strings); assertEquals(1, strings.size()); assertEquals("String value 1", strings.get(0)); @@ -256,7 +293,7 @@ public void testObjectsNestedInArraysRaw() { @Test - public void testCustomJsonbDeserializer() { + void testCustomJsonbDeserializer() { JsonObjectBuilder outerBuilder = jsonProvider.createObjectBuilder(); JsonObjectBuilder innerBuilder = jsonProvider.createObjectBuilder(); innerBuilder.add("first", "String value 1"); @@ -264,10 +301,425 @@ public void testCustomJsonbDeserializer() { outerBuilder.add("inner", innerBuilder.build()); JsonObject object = outerBuilder.build(); - YassonJsonb jsonb = (YassonJsonb) JsonbBuilder.create(new JsonbConfig().withDeserializers(new InnerPojoDeserializer())); - Pojo result = jsonb.fromJsonStructure(object, Pojo.class); - assertNotNull(result.getInner()); - assertEquals("String value 1", result.getInner().getInnerFirst()); - assertEquals("String value 2", result.getInner().getInnerSecond()); + testWithJsonbBuilderCreate(new JsonbConfig().withDeserializers(new InnerPojoDeserializer()), jsonb -> { + Pojo result = ((YassonJsonb)jsonb).fromJsonStructure(object, Pojo.class); + assertNotNull(result.getInner()); + assertEquals("String value 1", result.getInner().getInnerFirst()); + assertEquals("String value 2", result.getInner().getInnerSecond()); + }); + } + + private static void testWithParserAdapter(JsonObject jsonObject, Consumer consumer) { + try (JsonStructureToParserAdapter parser = new JsonStructureToParserAdapter(jsonObject, jsonProvider)) { + consumer.accept(parser); + } } + + @Nested + public class DirectParserTests { + + @Test + void testNumbers() { + testWithParserAdapter(jsonProvider.createObjectBuilder() + .add("int", 1) + .add("long", 1L) + .add("double", 1d) + .add("BigInteger", BigInteger.TEN) + .add("BigDecimal", BigDecimal.TEN) + .build(), parser -> { + parser.next(); + parser.next(); + parser.getString(); + parser.next(); + assertTrue(parser.isIntegralNumber()); + assertEquals(1, parser.getInt()); + + parser.next(); + parser.getString(); + parser.next(); + assertTrue(parser.isIntegralNumber()); + assertEquals(1L, parser.getLong()); + + parser.next(); + parser.getString(); + parser.next(); + assertFalse(parser.isIntegralNumber()); + assertEquals(BigDecimal.valueOf(1d), parser.getBigDecimal()); + + parser.next(); + parser.getString(); + parser.next(); + assertTrue(parser.isIntegralNumber()); + assertEquals(BigDecimal.TEN, parser.getBigDecimal()); + + parser.next(); + parser.getString(); + parser.next(); + assertTrue(parser.isIntegralNumber()); + assertEquals(BigDecimal.TEN, parser.getBigDecimal()); + }); + } + + @Test + void testParser_getString(){ + testWithParserAdapter(TestData.createFamilyPerson(), parser -> { + List values = new ArrayList<>(); + parser.next(); + while (parser.hasNext()) { + JsonParser.Event event = parser.next(); + if (GET_STRING_EVENT_ENUM_SET.contains(event)) { + String strValue = Objects.toString(parser.getString(), "null"); + values.add(strValue); + } + } + + assertThat(values,TestData.FAMILY_MATCHER_WITH_NO_QUOTATION); + }); + } + + @Test + void testParser_getValue(){ + testWithParserAdapter(TestData.createFamilyPerson(), parser -> { + List values = new ArrayList<>(); + parser.next(); + while (parser.hasNext()) { + JsonParser.Event event = parser.next(); + if (!NOT_GET_VALUE_EVENT_ENUM_SET.contains(event)) { + String strValue = Objects.toString(parser.getValue(), "null"); + values.add(strValue); + } + } + + assertThat(values, TestData.FAMILY_MATCHER_KEYS_WITH_QUOTATION); + }); + } + + @Test + void testSkipArray() { + testWithParserAdapter(TestData.createObjectWithArrays(), parser -> { + parser.next(); + parser.next(); + parser.getString(); + parser.next(); + parser.skipArray(); + parser.next(); + String key = parser.getString(); + + assertEquals("secondElement", key); + }); + } + + @Test + void testSkipObject() { + testWithParserAdapter(TestData.createJsonObject(), parser -> { + parser.next(); + parser.next(); + parser.getString(); + parser.next(); + parser.skipObject(); + parser.next(); + String key = parser.getString(); + + assertEquals("secondPerson", key); + }); + } + + private void assertThrowsIllegalStateException(Executable executable) { + assertThrows(IllegalStateException.class, executable); + } + + @Test + void testErrorGetObject() { + assertThrowsIllegalStateException(() -> testWithParserAdapter(TestData.createJsonObject(), JsonParser::getObject)); + } + + @Test + void testErrorGetArray() { + assertThrowsIllegalStateException(() -> testWithParserAdapter(TestData.createJsonObject(), parser -> { + parser.next(); + parser.getArray(); + })); + } + + @Test + void testErrorGetValueEndOfObject() { + assertThrowsIllegalStateException(() -> testWithParserAdapter(TestData.createJsonObject(), parser -> { + parser.next(); + parser.skipObject(); + parser.getValue(); + })); + } + + @Test + void testErrorGetValueEndOfArray() { + assertThrowsIllegalStateException(() -> testWithParserAdapter(TestData.createObjectWithArrays(), parser -> { + parser.next(); + parser.next(); + parser.getString(); + parser.next(); + parser.skipArray(); + parser.getValue(); + })); + } + + @Test + void testBooleanNullandCurrentEvent() { + testWithParserAdapter(Json.createObjectBuilder() + .add("true", true) + .add("false", false) + .addNull("null") + .build(), parser -> { + parser.next(); + parser.next(); + parser.getValue(); + parser.next(); + assertEquals(JsonValue.ValueType.TRUE, parser.getValue().getValueType()); + parser.next(); + parser.getValue(); + parser.next(); + assertEquals(JsonValue.ValueType.FALSE, parser.getValue().getValueType()); + parser.next(); + parser.getValue(); + parser.next(); + assertEquals(JsonValue.ValueType.NULL, parser.getValue().getValueType()); + assertEquals(JsonParser.Event.VALUE_NULL, parser.currentEvent()); + }); + } + } + + @Nested + public class StreamTests { + @Test + void testGetValueStream_GetOneElement() { + testWithParserAdapter(TestData.createFamilyPerson(), parser -> { + JsonString name = (JsonString) parser.getValueStream() + .map(JsonValue::asJsonObject) + .map(JsonObject::values) + .findFirst() + .orElseThrow() + .stream() + .filter(e -> e.getValueType() == JsonValue.ValueType.STRING) + .findFirst() + .orElseThrow(() -> new RuntimeException("Name not found")); + + assertEquals("John", name.getString()); + }); + } + + @Test + void testGetValueStream_GetList() { + testWithParserAdapter(TestData.createFamilyPerson(), parser -> { + List values = parser.getValueStream().map(value -> Objects.toString(value, "null")).collect(Collectors.toList()); + + assertThat(values, contains(TestData.JSON_FAMILY_STRING)); + }); + } + + @Test + void testGetArrayStream_GetOneElement() { + testWithParserAdapter(TestData.createObjectWithArrays(), parser -> { + parser.next(); + parser.next(); + String key = parser.getString(); + parser.next(); + JsonString element = (JsonString) parser.getArrayStream().filter(e -> e.getValueType() == JsonValue.ValueType.STRING) + .findFirst() + .orElseThrow(() -> new RuntimeException("Element not found")); + + assertEquals("first", element.getString()); + assertEquals("firstElement", key); + }); + } + + @Test + void testGetArrayStream_GetList() { + testWithParserAdapter(TestData.createObjectWithArrays(), parser -> { + parser.next(); + parser.next(); + String key = parser.getString(); + parser.next(); + List values = parser.getArrayStream().map(value -> Objects.toString(value, "null")).collect(Collectors.toList()); + + assertThat(values, TestData.ARRAY_STREAM_MATCHER); + assertEquals("firstElement", key); + }); + } + + @Test + void testGetObjectStream_GetOneElement() { + testWithParserAdapter(TestData.createJsonObject(), parser -> { + parser.next(); + String surname = parser.getObjectStream().filter(e -> e.getKey().equals("firstPerson")) + .map(Map.Entry::getValue) + .map(JsonValue::asJsonObject) + .map(obj -> obj.getString("surname")) + .findFirst() + .orElseThrow(() -> new RuntimeException("Surname not found")); + + assertEquals("Smith", surname); + }); + } + + @Test + void testGetObjectStream_GetList() { + testWithParserAdapter(TestData.createFamilyPerson(), parser -> { + parser.next(); + List values = parser.getObjectStream().collect(MAP_TO_LIST_COLLECTOR); + + assertThat(values, TestData.FAMILY_MATCHER_KEYS_WITHOUT_QUOTATION); + }); + } + } + + @Nested + public class JSONPStandardParserTests { + + private void testWithStringParser(String json, Consumer consumer) { + try (JsonParser parser = Json.createParser(new StringReader(json))) { + consumer.accept(parser); + } + } + + @Test + void testStandardStringParser_getValueStream() { + testWithStringParser(TestData.JSON_FAMILY_STRING, parser -> { + List values = parser.getValueStream().map(value -> Objects.toString(value, "null")).collect(Collectors.toList()); + + assertThat(values, contains(TestData.JSON_FAMILY_STRING)); + }); + } + + @Test + void testStandardStringParser_getArrayStream() { + testWithStringParser("{\"firstElement\":[\"first\", \"second\"],\"secondElement\":[\"third\", \"fourth\"]}", parser -> { + parser.next(); + parser.next(); + String key = parser.getString(); + parser.next(); + List values = parser.getArrayStream().map(value -> Objects.toString(value, "null")).collect(Collectors.toList()); + + assertThat(values, TestData.ARRAY_STREAM_MATCHER); + assertEquals("firstElement", key); + }); + } + + @Test + void testStandardStringParser_getObjectStream() { + testWithStringParser(TestData.JSON_FAMILY_STRING, parser -> { + + parser.next(); + List values = parser.getObjectStream().collect(MAP_TO_LIST_COLLECTOR); + + assertThat(values, TestData.FAMILY_MATCHER_KEYS_WITHOUT_QUOTATION); + }); + } + + @Test + void testStandardStringParser_getValue() { + testWithStringParser(TestData.JSON_FAMILY_STRING, parser -> { + List values = new ArrayList<>(); + parser.next(); + while (parser.hasNext()) { + JsonParser.Event event = parser.next(); + if (!NOT_GET_VALUE_EVENT_ENUM_SET.contains(event)) { + String strValue = Objects.toString(parser.getValue(), "null"); + values.add(strValue); + } + } + + assertThat(values, TestData.FAMILY_MATCHER_KEYS_WITH_QUOTATION); + }); + } + + @Test + void testStandardStringParser_getString() { + testWithStringParser(TestData.JSON_FAMILY_STRING, parser -> { + List values = new ArrayList<>(); + parser.next(); + while (parser.hasNext()) { + JsonParser.Event event = parser.next(); + if (GET_STRING_EVENT_ENUM_SET.contains(event)) { + String strValue = Objects.toString(parser.getString(), "null"); + values.add(strValue); + } + } + + assertThat(values, TestData.FAMILY_MATCHER_WITH_NO_QUOTATION); + }); + } + + @Test + void testStandardStructureParser_getString() { + JsonParserFactory factory = Json.createParserFactory(Map.of()); + JsonObject jsonObject = TestData.createFamilyPerson(); + + try (JsonParser parser = factory.createParser(jsonObject)) { + List values = new ArrayList<>(); + parser.next(); + while (parser.hasNext()) { + JsonParser.Event event = parser.next(); + if (GET_STRING_EVENT_ENUM_SET.contains(event)) { + String strValue = Objects.toString(parser.getString(), "null"); + values.add(strValue); + } + } + + assertThat(values, TestData.FAMILY_MATCHER_WITH_NO_QUOTATION); + } + } + } + + private static class TestData { + private static final String JSON_FAMILY_STRING = "{\"name\":\"John\",\"surname\":\"Smith\",\"age\":30,\"married\":true," + + "\"wife\":{\"name\":\"Deborah\",\"surname\":\"Harris\"},\"children\":[\"Jack\",\"Mike\"]}"; + + private static final Matcher> FAMILY_MATCHER_KEYS_WITHOUT_QUOTATION = + contains("name", "\"John\"", "surname", "\"Smith\"", "age", "30", "married", "true", "wife", + "{\"name\":\"Deborah\",\"surname\":\"Harris\"}", "children", "[\"Jack\",\"Mike\"]"); + + private static final Matcher> FAMILY_MATCHER_KEYS_WITH_QUOTATION = + contains("\"name\"", "\"John\"", "\"surname\"", "\"Smith\"", "\"age\"", "30", "\"married\"", "true", + "\"wife\"", "{\"name\":\"Deborah\",\"surname\":\"Harris\"}", "\"children\"", "[\"Jack\",\"Mike\"]"); + + private static final Matcher> FAMILY_MATCHER_WITH_NO_QUOTATION = + contains("name", "John", "surname", "Smith", "age", "30", "married", + "wife", "name", "Deborah", "surname", "Harris", "children", "Jack", "Mike"); + + private static final Matcher> ARRAY_STREAM_MATCHER = contains("\"first\"", "\"second\""); + + private static JsonObject createFamilyPerson() { + return jsonProvider.createObjectBuilder() + .add("name", "John") + .add("surname", "Smith") + .add("age", 30) + .add("married", true) + .add("wife", createPerson("Deborah", "Harris")) + .add("children", createArray("Jack", "Mike")) + .build(); + } + + private static JsonObject createObjectWithArrays() { + return jsonProvider.createObjectBuilder() + .add("firstElement", createArray("first", "second")) + .add("secondElement", createArray("third", "fourth")) + .build(); + } + + private static JsonArrayBuilder createArray(String firstElement, String secondElement) { + return jsonProvider.createArrayBuilder().add(firstElement).add(secondElement); + } + + private static JsonObject createJsonObject() { + return jsonProvider.createObjectBuilder() + .add("firstPerson", createPerson("John", "Smith")) + .add("secondPerson", createPerson("Deborah", "Harris")) + .build(); + } + + private static JsonObjectBuilder createPerson(String name, String surname) { + return jsonProvider.createObjectBuilder() + .add("name", name) + .add("surname", surname); + } + } } diff --git a/src/test/java/org/eclipse/yasson/serializers/MapToEntriesArraySerializerTest.java b/src/test/java/org/eclipse/yasson/serializers/MapToEntriesArraySerializerTest.java index 3961dd2af..dc5628796 100644 --- a/src/test/java/org/eclipse/yasson/serializers/MapToEntriesArraySerializerTest.java +++ b/src/test/java/org/eclipse/yasson/serializers/MapToEntriesArraySerializerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024 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 @@ -13,6 +13,8 @@ package org.eclipse.yasson.serializers; import org.junit.jupiter.api.*; + +import static org.eclipse.yasson.Jsonbs.testWithJsonbBuilderCreate; import static org.junit.jupiter.api.Assertions.*; import java.io.StringReader; @@ -30,8 +32,6 @@ import jakarta.json.JsonArray; import jakarta.json.JsonObject; import jakarta.json.JsonValue; -import jakarta.json.bind.Jsonb; -import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.serializer.DeserializationContext; import jakarta.json.bind.serializer.JsonbDeserializer; @@ -86,7 +86,7 @@ public int compare(Number n1, Number n2) { * @param source source Map for value verification * @param key Map key used to retrieve value */ - private static final void verifyMapValues(JsonObject jentry, Map source, K key) { + private static void verifyMapValues(JsonObject jentry, Map source, K key) { assertNotNull(jentry); assertNotNull(source); assertNotNull(key); @@ -129,7 +129,7 @@ private static final void verifyMapValues(JsonObject jentry, Map sour * @param jentry parsed Map entry as JsonObject * @param sourceEntry source Map entry for value verification */ - private static final void verifyMapValues(JsonObject jentry, Map.Entry sourceEntry) { + private static void verifyMapValues(JsonObject jentry, Map.Entry sourceEntry) { assertNotNull(jentry); assertNotNull(sourceEntry); switch (jentry.getValue("/value").getValueType()) { @@ -143,8 +143,8 @@ private static final void verifyMapValues(JsonObject jentry, Map.Entry 0); - verifyMapArrayValue(jentry, valueArray, sourceEntry); + assertFalse(valueArray.isEmpty()); + verifyMapArrayValue(/*jentry, */valueArray, sourceEntry); break; case STRING: String valueString = jentry.getString("value"); @@ -177,7 +177,7 @@ private static final void verifyMapValues(JsonObject jentry, Map.Entry Map.Entry getMapEntryForArrayKey(Map source, K[] key, Comparator cmp, Set keys) { + private static Map.Entry getMapEntryForArrayKey(Map source, K[] key, Comparator cmp, Set keys) { for (Map.Entry entry : source.entrySet()) { K[] sourceKey = entry.getKey(); boolean match = key.length == sourceKey.length; @@ -191,7 +191,7 @@ private static final Map.Entry getMapEntryForArrayKey(Map } // Matching key is removed from Set for key processing check if (match) { - keys.remove(entry.getKey()); + keys.remove(sourceKey); return entry; } } @@ -206,11 +206,11 @@ private static final Map.Entry getMapEntryForArrayKey(Map * @param sourceValue source Map value * @param cmp optional comparator to use for verification */ - private static final void verifyMapArrayValues(V[] value, V[] sourceValue, Comparator cmp) { + private static void verifyMapArrayValues(V[] value, V[] sourceValue, Comparator cmp) { assertEquals(sourceValue.length, value.length); for (int i = 0; i < sourceValue.length; i++) { if (cmp != null) { - assertTrue(cmp.compare(sourceValue[i], value[i]) == 0); + assertEquals(0, cmp.compare(sourceValue[i], value[i])); } else { assertEquals(sourceValue[i], value[i]); } @@ -220,11 +220,10 @@ private static final void verifyMapArrayValues(V[] value, V[] sourceValue, C /** * Build Map key as an array. Get corresponding key from source Map. * - * @param jentry Map key parsed as JsonArray * @param sourceEntry source Map */ @SuppressWarnings("unchecked") - private static final void verifyMapArrayValue(JsonObject jentry, final JsonArray valueArray, Map.Entry sourceEntry) { + private static void verifyMapArrayValue(/*JsonObject jentry, */final JsonArray valueArray, Map.Entry sourceEntry) { int size = valueArray.size(); // All array elements in the tests are of the same type. switch (valueArray.get(0).getValueType()) { @@ -270,7 +269,7 @@ private static final void verifyMapArrayValue(JsonObject jentry, final Jso * @param source source Map */ @SuppressWarnings("unchecked") - private static final void verifyMapArrayKey(JsonObject jentry, final JsonArray keyArray, Map source, Set keys) { + private static void verifyMapArrayKey(JsonObject jentry, final JsonArray keyArray, Map source, Set keys) { int size = keyArray.size(); // All array elements in the tests are of the same type. switch (keyArray.get(0).getValueType()) { @@ -320,7 +319,7 @@ private static final void verifyMapArrayKey(JsonObject jentry, final JsonA * @param array serialized Map parsed and provided as JsonArray */ @SuppressWarnings("unchecked") - private static final void verifySerialization(Map source, JsonArray array) { + private static void verifySerialization(Map source, JsonArray array) { assertEquals(source.size(), array.size()); Set keys = source.keySet(); array.forEach(entry -> { @@ -337,7 +336,7 @@ private static final void verifySerialization(Map source, JsonArray break; case ARRAY: { JsonArray keyArray = jentry.getJsonArray("key"); - assertTrue(keyArray.size() > 0); + assertFalse(keyArray.isEmpty()); verifyMapArrayKey(jentry, keyArray, source, keys); } break; @@ -375,13 +374,14 @@ private static final void verifySerialization(Map source, JsonArray // @Test // public void testSerializeNumberStringMapToEntriesArray() { // Map map = new TreeMap<>(CMP_NUM); -// Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withFormatting(true)); +// testWithJsonbBuilderCreate(new JsonbConfig().withFormatting(true), jsonb -> { // map.put(Integer.valueOf(12), "twelve"); // map.put(Short.valueOf((short)48), "forty eight"); // map.put(Long.valueOf(256), "two hundred fifty-six"); // String json = jsonb.toJson(map); // JsonArray jarr = Json.createReader(new StringReader(json)).read().asJsonArray(); // verifySerialization(map, jarr); +// }); // } /** @@ -390,14 +390,15 @@ private static final void verifySerialization(Map source, JsonArray @Test public void testSerializePoJoPoJoMapToEntriesArray() { Map map = new HashMap<>(); - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withFormatting(true)); - map.put(new Trainer("John Smith", 35), new Pokemon("Charmander", "fire", 980)); - map.put(new Trainer("Tom Jones", 24), new Pokemon("Caterpie", "bug", 437)); - map.put(new Trainer("Peter Wright", 27), new Pokemon("Houndour", "dark", 1234)); - map.put(new Trainer("Bob Parker", 19), new Pokemon("Sneasel", "ice", 2051)); - String json = jsonb.toJson(map); - JsonArray jarr = Json.createReader(new StringReader(json)).read().asJsonArray(); - verifySerialization(map, jarr); + testWithJsonbBuilderCreate(new JsonbConfig().withFormatting(true), jsonb -> { + map.put(new Trainer("John Smith", 35), new Pokemon("Charmander", "fire", 980)); + map.put(new Trainer("Tom Jones", 24), new Pokemon("Caterpie", "bug", 437)); + map.put(new Trainer("Peter Wright", 27), new Pokemon("Houndour", "dark", 1234)); + map.put(new Trainer("Bob Parker", 19), new Pokemon("Sneasel", "ice", 2051)); + String json = jsonb.toJson(map); + JsonArray jarr = Json.createReader(new StringReader(json)).read().asJsonArray(); + verifySerialization(map, jarr); + }); } /** @@ -407,12 +408,13 @@ public void testSerializePoJoPoJoMapToEntriesArray() { public void testSerializeSimpleSimpleMapToEntriesArray() { String expected = "{\"false\":true,\"10\":24,\"Name\":\"John Smith\"}"; Map map = new HashMap<>(); - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()); - map.put("Name", "John Smith"); - map.put(Integer.valueOf(10), Long.valueOf(24l)); - map.put(Boolean.FALSE, Boolean.TRUE); - String json = jsonb.toJson(map); - assertEquals(expected, json); + testWithJsonbBuilderCreate(new JsonbConfig(), jsonb -> { + map.put("Name", "John Smith"); + map.put(10, 24L); + map.put(Boolean.FALSE, Boolean.TRUE); + String json = jsonb.toJson(map); + assertEquals(expected, json); + }); } /** @@ -421,15 +423,16 @@ public void testSerializeSimpleSimpleMapToEntriesArray() { @Test public void testSerializeSimpleArraySimpleArrayMapToEntriesArray() { Map map = new HashMap<>(); - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withFormatting(true)); - map.put(new String[] {"John", "Smith"}, new String[] {"first name", "second name"}); - map.put(new String[] {"Pikachu", "electric"}, new String[] {"pokemon name", "pokemon type"}); - map.put( - new Trainer[] {new Trainer("Bob", 15), new Trainer("Ash", 12)}, - new Pokemon[] {new Pokemon("Charmander", "fire", 1245), new Pokemon("Kyogre", "water", 3056)}); - String json = jsonb.toJson(map); - JsonArray jarr = Json.createReader(new StringReader(json)).read().asJsonArray(); - verifySerialization(map, jarr); + testWithJsonbBuilderCreate(new JsonbConfig().withFormatting(true), jsonb -> { + map.put(new String[] {"John", "Smith"}, new String[] {"first name", "second name"}); + map.put(new String[] {"Pikachu", "electric"}, new String[] {"pokemon name", "pokemon type"}); + map.put( + new Trainer[] {new Trainer("Bob", 15), new Trainer("Ash", 12)}, + new Pokemon[] {new Pokemon("Charmander", "fire", 1245), new Pokemon("Kyogre", "water", 3056)}); + String json = jsonb.toJson(map); + JsonArray jarr = Json.createReader(new StringReader(json)).read().asJsonArray(); + verifySerialization(map, jarr); + }); } /** @@ -453,34 +456,35 @@ public void testDeSerializePrimitivesMapToEntriesArray() { " \"value\": 21" + " }" + "]"; - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()); - Map map = jsonb.fromJson(jsonString, Map.class); - assertEquals(3, map.size()); - // Make sure that all 3 pokemons were checked. - int valueCheck = 0x00; - for (Map.Entry entry : map.entrySet()) { - if ((entry.getKey() instanceof String) && "first".equals(entry.getKey())) { - assertEquals("Peter Parker", entry.getValue()); - valueCheck |= 0x01; - } - if ((entry.getKey() instanceof Number) && entry.getKey().equals(new BigDecimal(42))) { - assertEquals(true, entry.getValue()); - valueCheck |= 0x02; - } - if ((entry.getKey() instanceof Boolean) && entry.getKey().equals(false)) { - assertEquals(new BigDecimal(21), entry.getValue()); - valueCheck |= 0x04; + testWithJsonbBuilderCreate(new JsonbConfig(), jsonb -> { + Map map = jsonb.fromJson(jsonString, Map.class); + assertEquals(3, map.size()); + // Make sure that all 3 pokemons were checked. + int valueCheck = 0x00; + for (Map.Entry entry : map.entrySet()) { + if ((entry.getKey() instanceof String) && "first".equals(entry.getKey())) { + assertEquals("Peter Parker", entry.getValue()); + valueCheck |= 0x01; + } + if ((entry.getKey() instanceof Number) && entry.getKey().equals(new BigDecimal(42))) { + assertEquals(true, entry.getValue()); + valueCheck |= 0x02; + } + if ((entry.getKey() instanceof Boolean) && entry.getKey().equals(false)) { + assertEquals(new BigDecimal(21), entry.getValue()); + valueCheck |= 0x04; + } } - } - if ((valueCheck & 0x01) == 0) { - fail("Did not find key \"first\" in the Map"); - } - if ((valueCheck & 0x02) == 0) { - fail("Did not find key 42 in the Map"); - } - if ((valueCheck & 0x04) == 0) { - fail("Did not find key false in the Map"); - } + if ((valueCheck & 0x01) == 0) { + fail("Did not find key \"first\" in the Map"); + } + if ((valueCheck & 0x02) == 0) { + fail("Did not find key 42 in the Map"); + } + if ((valueCheck & 0x04) == 0) { + fail("Did not find key false in the Map"); + } + }); } /** @@ -531,41 +535,42 @@ public Type getOwnerType() { return null; } }; - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()); - Map map = jsonb.fromJson(jsonString, pt); - assertEquals(3, map.size()); - // Make sure that all 3 pokemons were checked. - int valueCheck = 0x00; - for (Map.Entry entry : map.entrySet()) { - Pokemon pokemon = entry.getValue(); - if ("Pikachu".equals(entry.getKey())) { - assertEquals("Pikachu", pokemon.name); - assertEquals("electric", pokemon.type); - assertEquals(456, pokemon.cp); - valueCheck |= 0x01; - } - if ("Squirtle".equals(entry.getKey())) { - assertEquals("Squirtle", pokemon.name); - assertEquals("water", pokemon.type); - assertEquals(124, pokemon.cp); - valueCheck |= 0x02; - } - if ("Rayquaza".equals(entry.getKey())) { - assertEquals("Rayquaza", pokemon.name); - assertEquals("dragon", pokemon.type); - assertEquals(3273, pokemon.cp); - valueCheck |= 0x04; + testWithJsonbBuilderCreate(new JsonbConfig(), jsonb -> { + Map map = jsonb.fromJson(jsonString, pt); + assertEquals(3, map.size()); + // Make sure that all 3 pokemons were checked. + int valueCheck = 0x00; + for (Map.Entry entry : map.entrySet()) { + Pokemon pokemon = entry.getValue(); + if ("Pikachu".equals(entry.getKey())) { + assertEquals("Pikachu", pokemon.name); + assertEquals("electric", pokemon.type); + assertEquals(456, pokemon.cp); + valueCheck |= 0x01; + } + if ("Squirtle".equals(entry.getKey())) { + assertEquals("Squirtle", pokemon.name); + assertEquals("water", pokemon.type); + assertEquals(124, pokemon.cp); + valueCheck |= 0x02; + } + if ("Rayquaza".equals(entry.getKey())) { + assertEquals("Rayquaza", pokemon.name); + assertEquals("dragon", pokemon.type); + assertEquals(3273, pokemon.cp); + valueCheck |= 0x04; + } } - } - if ((valueCheck & 0x01) == 0) { - fail("Did not find key \"Pikachu\" in the Map"); - } - if ((valueCheck & 0x02) == 0) { - fail("Did not find key \"Squirtle\" in the Map"); - } - if ((valueCheck & 0x04) == 0) { - fail("Did not find key \"Rayquaza\" in the Map"); - } + if ((valueCheck & 0x01) == 0) { + fail("Did not find key \"Pikachu\" in the Map"); + } + if ((valueCheck & 0x02) == 0) { + fail("Did not find key \"Squirtle\" in the Map"); + } + if ((valueCheck & 0x04) == 0) { + fail("Did not find key \"Rayquaza\" in the Map"); + } + }); } /** @@ -624,45 +629,46 @@ public Type getOwnerType() { return null; } }; - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()); - Map map = jsonb.fromJson(jsonString, pt); - assertEquals(3, map.size()); - // Make sure that all 3 pokemons were checked. - int valueCheck = 0x00; - for (Map.Entry entry : map.entrySet()) { - Trainer trainer = entry.getKey(); - Pokemon pokemon = entry.getValue(); - if ("Bob".equals(trainer.name)) { - assertEquals(12, trainer.age); - assertEquals("Pikachu", pokemon.name); - assertEquals("electric", pokemon.type); - assertEquals(456, pokemon.cp); - valueCheck |= 0x01; - } - if ("Ash".equals(trainer.name)) { - assertEquals(10, trainer.age); - assertEquals("Squirtle", pokemon.name); - assertEquals("water", pokemon.type); - assertEquals(124, pokemon.cp); - valueCheck |= 0x02; - } - if ("Joe".equals(trainer.name)) { - assertEquals(15, trainer.age); - assertEquals("Rayquaza", pokemon.name); - assertEquals("dragon", pokemon.type); - assertEquals(3273, pokemon.cp); - valueCheck |= 0x04; + testWithJsonbBuilderCreate(new JsonbConfig(), jsonb -> { + Map map = jsonb.fromJson(jsonString, pt); + assertEquals(3, map.size()); + // Make sure that all 3 pokemons were checked. + int valueCheck = 0x00; + for (Map.Entry entry : map.entrySet()) { + Trainer trainer = entry.getKey(); + Pokemon pokemon = entry.getValue(); + if ("Bob".equals(trainer.name)) { + assertEquals(12, trainer.age); + assertEquals("Pikachu", pokemon.name); + assertEquals("electric", pokemon.type); + assertEquals(456, pokemon.cp); + valueCheck |= 0x01; + } + if ("Ash".equals(trainer.name)) { + assertEquals(10, trainer.age); + assertEquals("Squirtle", pokemon.name); + assertEquals("water", pokemon.type); + assertEquals(124, pokemon.cp); + valueCheck |= 0x02; + } + if ("Joe".equals(trainer.name)) { + assertEquals(15, trainer.age); + assertEquals("Rayquaza", pokemon.name); + assertEquals("dragon", pokemon.type); + assertEquals(3273, pokemon.cp); + valueCheck |= 0x04; + } } - } - if ((valueCheck & 0x01) == 0) { - fail("Did not find key \"Bob\" in the Map"); - } - if ((valueCheck & 0x02) == 0) { - fail("Did not find key \"Ash\" in the Map"); - } - if ((valueCheck & 0x04) == 0) { - fail("Did not find key \"Joe\" in the Map"); - } + if ((valueCheck & 0x01) == 0) { + fail("Did not find key \"Bob\" in the Map"); + } + if ((valueCheck & 0x02) == 0) { + fail("Did not find key \"Ash\" in the Map"); + } + if ((valueCheck & 0x04) == 0) { + fail("Did not find key \"Joe\" in the Map"); + } + }); } /** @@ -703,31 +709,32 @@ public Type getOwnerType() { return null; } }; - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()); - Map map = jsonb.fromJson(jsonString, pt); - assertEquals(2, map.size()); - // Make sure that all map entries were checked. - int valueCheck = 0x00; - for (Map.Entry entry : map.entrySet()) { - Integer[] key = entry.getKey(); - String[] value = entry.getValue(); - if (key[0] == 1 && key[1] == 2) { - assertEquals("Bob" ,value[0]); - assertEquals("Tom" ,value[1]); - valueCheck |= 0x01; - } - if (key[0] == 3 && key[1] == 4) { - assertEquals("John" ,value[0]); - assertEquals("Greg" ,value[1]); - valueCheck |= 0x02; + testWithJsonbBuilderCreate(new JsonbConfig(), jsonb -> { + Map map = jsonb.fromJson(jsonString, pt); + assertEquals(2, map.size()); + // Make sure that all map entries were checked. + int valueCheck = 0x00; + for (Map.Entry entry : map.entrySet()) { + Integer[] key = entry.getKey(); + String[] value = entry.getValue(); + if (key[0] == 1 && key[1] == 2) { + assertEquals("Bob", value[0]); + assertEquals("Tom", value[1]); + valueCheck |= 0x01; + } + if (key[0] == 3 && key[1] == 4) { + assertEquals("John", value[0]); + assertEquals("Greg", value[1]); + valueCheck |= 0x02; + } } - } - if ((valueCheck & 0x01) == 0) { - fail("Did not find key [1,2] in the Map"); - } - if ((valueCheck & 0x02) == 0) { - fail("Did not find key [3,4] in the Map"); - } + if ((valueCheck & 0x01) == 0) { + fail("Did not find key [1,2] in the Map"); + } + if ((valueCheck & 0x02) == 0) { + fail("Did not find key [3,4] in the Map"); + } + }); } /** @@ -797,42 +804,43 @@ public Type getOwnerType() { return null; } }; - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()); - Map map = jsonb.fromJson(jsonString, pt); - assertEquals(2, map.size()); - int valueCheck = 0x00; - for (Map.Entry entry : map.entrySet()) { - Trainer[] key = entry.getKey(); - Pokemon[] value = entry.getValue(); - if (key[0].name.equals("Ash") && key[1].name.equals("Joe")) { - assertEquals(12, key[0].age); - assertEquals(14, key[1].age); - assertEquals("Rayquaza", value[0].name); - assertEquals("dragon", value[0].type); - assertEquals(3273, value[0].cp); - assertEquals("Tyranitar", value[1].name); - assertEquals("dark", value[1].type); - assertEquals(3181, value[1].cp); - valueCheck |= 0x01; - } - if (key[0].name.equals("Bob") && key[1].name.equals("Maggie")) { - assertEquals(13, key[0].age); - assertEquals(15, key[1].age); - assertEquals("Raikou", value[0].name); - assertEquals("electric", value[0].type); - assertEquals(3095, value[0].cp); - assertEquals("Mamoswine", value[1].name); - assertEquals("ice", value[1].type); - assertEquals(3055, value[1].cp); - valueCheck |= 0x02; + testWithJsonbBuilderCreate(new JsonbConfig(), jsonb -> { + Map map = jsonb.fromJson(jsonString, pt); + assertEquals(2, map.size()); + int valueCheck = 0x00; + for (Map.Entry entry : map.entrySet()) { + Trainer[] key = entry.getKey(); + Pokemon[] value = entry.getValue(); + if (key[0].name.equals("Ash") && key[1].name.equals("Joe")) { + assertEquals(12, key[0].age); + assertEquals(14, key[1].age); + assertEquals("Rayquaza", value[0].name); + assertEquals("dragon", value[0].type); + assertEquals(3273, value[0].cp); + assertEquals("Tyranitar", value[1].name); + assertEquals("dark", value[1].type); + assertEquals(3181, value[1].cp); + valueCheck |= 0x01; + } + if (key[0].name.equals("Bob") && key[1].name.equals("Maggie")) { + assertEquals(13, key[0].age); + assertEquals(15, key[1].age); + assertEquals("Raikou", value[0].name); + assertEquals("electric", value[0].type); + assertEquals(3095, value[0].cp); + assertEquals("Mamoswine", value[1].name); + assertEquals("ice", value[1].type); + assertEquals(3055, value[1].cp); + valueCheck |= 0x02; + } } - } - if ((valueCheck & 0x01) == 0) { - fail("Did not find key with \"Ash\" and \"Joe\" in the Map"); - } - if ((valueCheck & 0x02) == 0) { - fail("Did not find key with \"Bob\" and \"Maggie\" in the Map"); - } + if ((valueCheck & 0x01) == 0) { + fail("Did not find key with \"Ash\" and \"Joe\" in the Map"); + } + if ((valueCheck & 0x02) == 0) { + fail("Did not find key with \"Bob\" and \"Maggie\" in the Map"); + } + }); } public static class LocaleSerializer implements JsonbSerializer { @@ -890,7 +898,7 @@ public String toString() { } } - public static class MapObjectLocaleString extends MapObject {}; + public static class MapObjectLocaleString extends MapObject {} private void verifyMapObjectLocaleStringSerialization(JsonObject jsonObject, MapObjectLocaleString mapObject) { // Expected serialization is: {"values":[{"key":"lang-tag","value":"string"},...]} @@ -918,20 +926,21 @@ private void verifyMapObjectLocaleStringSerialization(JsonObject jsonObject, Map */ @Test public void testMapLocaleString() { - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig() + testWithJsonbBuilderCreate(new JsonbConfig() .withSerializers(new LocaleSerializer()) - .withDeserializers(new LocaleDeserializer())); + .withDeserializers(new LocaleDeserializer()), jsonb -> { - MapObjectLocaleString mapObject = new MapObjectLocaleString(); - mapObject.getValues().put(Locale.US, "us"); - mapObject.getValues().put(Locale.ENGLISH, "en"); - mapObject.getValues().put(Locale.JAPAN, "jp"); + MapObjectLocaleString mapObject = new MapObjectLocaleString(); + mapObject.getValues().put(Locale.US, "us"); + mapObject.getValues().put(Locale.ENGLISH, "en"); + mapObject.getValues().put(Locale.JAPAN, "jp"); - String json = jsonb.toJson(mapObject); - JsonObject jsonObject = Json.createReader(new StringReader(json)).read().asJsonObject(); - verifyMapObjectLocaleStringSerialization(jsonObject, mapObject); + String json = jsonb.toJson(mapObject); + JsonObject jsonObject = Json.createReader(new StringReader(json)).read().asJsonObject(); + verifyMapObjectLocaleStringSerialization(jsonObject, mapObject); - MapObjectLocaleString resObject = jsonb.fromJson(json, MapObjectLocaleString.class); - assertEquals(mapObject, resObject); + MapObjectLocaleString resObject = jsonb.fromJson(json, MapObjectLocaleString.class); + assertEquals(mapObject, resObject); + }); } } diff --git a/src/test/java/org/eclipse/yasson/serializers/MapToObjectSerializerTest.java b/src/test/java/org/eclipse/yasson/serializers/MapToObjectSerializerTest.java index 796f3d1f7..fa77f2767 100644 --- a/src/test/java/org/eclipse/yasson/serializers/MapToObjectSerializerTest.java +++ b/src/test/java/org/eclipse/yasson/serializers/MapToObjectSerializerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024 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 @@ -12,14 +12,14 @@ package org.eclipse.yasson.serializers; -import jakarta.json.bind.Jsonb; -import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import org.junit.jupiter.api.Test; import static org.eclipse.yasson.Assertions.shouldFail; +import static org.eclipse.yasson.Jsonbs.testWithJsonbBuilderCreate; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertTrue; import java.math.BigInteger; @@ -40,10 +40,10 @@ public class MapToObjectSerializerTest { enum TestEnum { ONE, TWO; - @Override /** * Force to lower case to check toString is not used during serialization of maps */ + @Override public String toString() { return this.name().toLowerCase(); } @@ -90,29 +90,30 @@ public String toString() { } } - public static class MapObjectIntegerString extends MapObject {}; + public static class MapObjectIntegerString extends MapObject {} - public static class MapObjectBigIntegerString extends MapObject {}; + public static class MapObjectBigIntegerString extends MapObject {} - public static class MapObjectEnumString extends MapObject {}; + public static class MapObjectEnumString extends MapObject {} - public static class MapObjectStringString extends MapObject {}; + public static class MapObjectStringString extends MapObject {} - public static class MapObjectBooleanString extends MapObject {}; + public static class MapObjectBooleanString extends MapObject {} - /** + /** * Test serialization of Map with Number keys and String values. */ @Test public void testSerializeEnumMapToObject() { Map map = new EnumMap<>(TestEnum.class); - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withFormatting(true)); - map.put(TestEnum.ONE, "value1"); - map.put(TestEnum.TWO, "value2"); - String json = jsonb.toJson(map); - for (TestEnum e : TestEnum.values()) { - assertTrue(json.contains(e.name()), "Enumeration not well serialized"); - } + testWithJsonbBuilderCreate(new JsonbConfig().withFormatting(true), jsonb -> { + map.put(TestEnum.ONE, "value1"); + map.put(TestEnum.TWO, "value2"); + String json = jsonb.toJson(map); + for (TestEnum e : TestEnum.values()) { + assertTrue(json.contains(e.name()), "Enumeration not well serialized"); + } + }); } /** @@ -120,16 +121,17 @@ public void testSerializeEnumMapToObject() { */ @Test public void testIntegerString() { - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()); + testWithJsonbBuilderCreate(new JsonbConfig(), jsonb -> { - MapObjectIntegerString mapObject = new MapObjectIntegerString(); - mapObject.getValues().put(12, "twelve"); - mapObject.getValues().put(48, "forty eight"); - mapObject.getValues().put(256, "two hundred fifty-six"); + MapObjectIntegerString mapObject = new MapObjectIntegerString(); + mapObject.getValues().put(12, "twelve"); + mapObject.getValues().put(48, "forty eight"); + mapObject.getValues().put(256, "two hundred fifty-six"); - String json = jsonb.toJson(mapObject); - MapObjectIntegerString resObject = jsonb.fromJson(json, MapObjectIntegerString.class); - assertEquals(mapObject, resObject); + String json = jsonb.toJson(mapObject); + MapObjectIntegerString resObject = jsonb.fromJson(json, MapObjectIntegerString.class); + assertEquals(mapObject, resObject); + }); } /** @@ -137,16 +139,17 @@ public void testIntegerString() { */ @Test public void testBigIntegerString() { - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()); + testWithJsonbBuilderCreate(new JsonbConfig(), jsonb -> { - MapObjectBigIntegerString mapObject = new MapObjectBigIntegerString(); - mapObject.getValues().put(new BigInteger("12"), "twelve"); - mapObject.getValues().put(new BigInteger("48"), "forty eight"); - mapObject.getValues().put(new BigInteger("256"), "two hundred fifty-six"); + MapObjectBigIntegerString mapObject = new MapObjectBigIntegerString(); + mapObject.getValues().put(new BigInteger("12"), "twelve"); + mapObject.getValues().put(new BigInteger("48"), "forty eight"); + mapObject.getValues().put(new BigInteger("256"), "two hundred fifty-six"); - String json = jsonb.toJson(mapObject); - MapObjectBigIntegerString resObject = jsonb.fromJson(json, MapObjectBigIntegerString.class); - assertEquals(mapObject, resObject); + String json = jsonb.toJson(mapObject); + MapObjectBigIntegerString resObject = jsonb.fromJson(json, MapObjectBigIntegerString.class); + assertEquals(mapObject, resObject); + }); } /** @@ -154,15 +157,16 @@ public void testBigIntegerString() { */ @Test public void testEnumString() { - Jsonb jsonb = JsonbBuilder.create(); + testWithJsonbBuilderCreate(jsonb -> { - MapObjectEnumString mapObject = new MapObjectEnumString(); - mapObject.getValues().put(TestEnum.ONE, "one"); - mapObject.getValues().put(TestEnum.TWO, "two"); + MapObjectEnumString mapObject = new MapObjectEnumString(); + mapObject.getValues().put(TestEnum.ONE, "one"); + mapObject.getValues().put(TestEnum.TWO, "two"); - String json = jsonb.toJson(mapObject); - MapObjectEnumString resObject = jsonb.fromJson(json, MapObjectEnumString.class); - assertEquals(mapObject, resObject); + String json = jsonb.toJson(mapObject); + MapObjectEnumString resObject = jsonb.fromJson(json, MapObjectEnumString.class); + assertEquals(mapObject, resObject); + }); } /** @@ -170,15 +174,16 @@ public void testEnumString() { */ @Test public void testStringString() { - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty("lala", "lala")); + testWithJsonbBuilderCreate(new JsonbConfig().setProperty("lala", "lala"), jsonb -> { - MapObjectStringString mapObject = new MapObjectStringString(); - mapObject.getValues().put("one", "one"); - mapObject.getValues().put("two", "two"); + MapObjectStringString mapObject = new MapObjectStringString(); + mapObject.getValues().put("one", "one"); + mapObject.getValues().put("two", "two"); - String json = jsonb.toJson(mapObject); - MapObjectStringString resObject = jsonb.fromJson(json, MapObjectStringString.class); - assertEquals(mapObject, resObject); + String json = jsonb.toJson(mapObject); + MapObjectStringString resObject = jsonb.fromJson(json, MapObjectStringString.class); + assertEquals(mapObject, resObject); + }); } /** @@ -186,17 +191,18 @@ public void testStringString() { */ @Test public void testNotParametrizedMap() { - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()); - - Map mapObject = new HashMap<>(); - mapObject.put(12, "twelve"); - mapObject.put(48, "forty eight"); - mapObject.put(256, "two hundred fifty-six"); - - String json = jsonb.toJson(mapObject); - Map resObject = jsonb.fromJson(json, Map.class); - assertEquals(3, resObject.size()); - assertTrue(resObject.keySet().iterator().next() instanceof String); + testWithJsonbBuilderCreate(new JsonbConfig(), jsonb -> { + + Map mapObject = new HashMap<>(); + mapObject.put(12, "twelve"); + mapObject.put(48, "forty eight"); + mapObject.put(256, "two hundred fifty-six"); + + String json = jsonb.toJson(mapObject); + Map resObject = jsonb.fromJson(json, Map.class); + assertEquals(3, resObject.size()); + assertInstanceOf(String.class, resObject.keySet().iterator().next()); + }); } /** @@ -206,13 +212,14 @@ public void testNotParametrizedMap() { */ @Test public void testBooleanStringMapToObjectSerializer() { - Jsonb jsonb = JsonbBuilder.create(); - - String json = "{\"values\":{\"true\":\"TRUE\",\"false\":\"FALSE\"}}"; - MapObjectBooleanString resObject = jsonb.fromJson(json, MapObjectBooleanString.class); - assertEquals(2, resObject.getValues().size()); - assertEquals("TRUE", resObject.getValues().get(true)); - assertEquals("FALSE", resObject.getValues().get(false)); + testWithJsonbBuilderCreate(jsonb -> { + + String json = "{\"values\":{\"true\":\"TRUE\",\"false\":\"FALSE\"}}"; + MapObjectBooleanString resObject = jsonb.fromJson(json, MapObjectBooleanString.class); + assertEquals(2, resObject.getValues().size()); + assertEquals("TRUE", resObject.getValues().get(true)); + assertEquals("FALSE", resObject.getValues().get(false)); + }); } /** @@ -221,9 +228,10 @@ public void testBooleanStringMapToObjectSerializer() { */ @Test public void testIncorrectTypeMapToObjectSerializer() { - Jsonb jsonb = JsonbBuilder.create(); + testWithJsonbBuilderCreate(jsonb -> { - String json = "{\"values\":{\"1\":\"OK\",\"error\":\"KO\"}}"; - shouldFail(() -> jsonb.fromJson(json, MapObjectIntegerString.class)); + String json = "{\"values\":{\"1\":\"OK\",\"error\":\"KO\"}}"; + shouldFail(() -> jsonb.fromJson(json, MapObjectIntegerString.class)); + }); } } diff --git a/src/test/java/org/eclipse/yasson/serializers/SerializersTest.java b/src/test/java/org/eclipse/yasson/serializers/SerializersTest.java index db0efdf18..ea4d5ffca 100644 --- a/src/test/java/org/eclipse/yasson/serializers/SerializersTest.java +++ b/src/test/java/org/eclipse/yasson/serializers/SerializersTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024 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 @@ -24,6 +24,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.NoSuchElementException; import java.util.Objects; import java.util.SortedMap; import java.util.TimeZone; @@ -31,8 +32,6 @@ import jakarta.json.Json; import jakarta.json.JsonObject; -import jakarta.json.bind.Jsonb; -import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.JsonbException; import jakarta.json.bind.config.PropertyOrderStrategy; @@ -51,6 +50,8 @@ import org.eclipse.yasson.serializers.model.Author; import org.eclipse.yasson.serializers.model.Box; import org.eclipse.yasson.serializers.model.BoxWithAnnotations; +import org.eclipse.yasson.serializers.model.Cars; +import org.eclipse.yasson.serializers.model.Colors; import org.eclipse.yasson.serializers.model.Containee; import org.eclipse.yasson.serializers.model.Container; import org.eclipse.yasson.serializers.model.Crate; @@ -63,22 +64,29 @@ import org.eclipse.yasson.serializers.model.ExplicitJsonbSerializer; import org.eclipse.yasson.serializers.model.GenericPropertyPojo; import org.eclipse.yasson.serializers.model.ImplicitJsonbSerializer; +import org.eclipse.yasson.serializers.model.JsonParserTestDeserializers; +import org.eclipse.yasson.serializers.model.JsonParserTestPojo; import org.eclipse.yasson.serializers.model.NumberDeserializer; import org.eclipse.yasson.serializers.model.NumberSerializer; +import org.eclipse.yasson.serializers.model.TwoObjectsComparer; import org.eclipse.yasson.serializers.model.RecursiveDeserializer; import org.eclipse.yasson.serializers.model.RecursiveSerializer; import org.eclipse.yasson.serializers.model.SimpleAnnotatedSerializedArrayContainer; import org.eclipse.yasson.serializers.model.SimpleContainer; import org.eclipse.yasson.serializers.model.StringWrapper; import org.eclipse.yasson.serializers.model.SupertypeSerializerPojo; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import static java.util.Collections.singletonMap; import static org.eclipse.yasson.Jsonbs.defaultJsonb; import static org.eclipse.yasson.Jsonbs.nullableJsonb; +import static org.eclipse.yasson.Jsonbs.testWithJsonbBuilderCreate; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.hamcrest.CoreMatchers.is; @@ -105,6 +113,18 @@ public void serialize(Box box, JsonGenerator out, SerializationContext ctx) { out.write(box.secondBoxStr); out.writeEnd(); } + }; + + private static final JsonbSerializer BOX_ARRAY_SERIALIZER_CHAINED_AS_LAMBDA = (box, out, ctx) -> out.writeStartArray() + .write(box.boxStr) + .write(box.secondBoxStr) + .writeEnd(); + + private static final JsonbSerializer BOX_ARRAY_SERIALIZER_AS_LAMBDA = (box, out, ctx) -> { + out.writeStartArray(); + out.write(box.boxStr); + out.write(box.secondBoxStr); + out.writeEnd(); }; @Test @@ -160,25 +180,25 @@ public void testClassLevelAnnotationOnGenericRoot() { */ @Test public void testDeserializerDeserializationByType() { - JsonbConfig config = new JsonbConfig().withDeserializers(new CrateDeserializer()); - Jsonb jsonb = JsonbBuilder.create(config); + testWithJsonbBuilderCreate(new JsonbConfig().withDeserializers(new CrateDeserializer()), jsonb -> { - Box box = createPojoWithDates(); + Box box = createPojoWithDates(); - String expected = "{\"boxStr\":\"Box string\",\"crate\":{\"crateInner\":{\"crateInnerBigDec\":10,\"crate_inner_str\":\"Single inner\",\"date\":\"14.05.2015 || 11:10:01\"},\"crateInnerList\":[{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 0\"},{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 1\"}],\"date\":\"2015-05-14T11:10:01\"},\"secondBoxStr\":\"Second box string\"}"; + String expected = "{\"boxStr\":\"Box string\",\"crate\":{\"crateInner\":{\"crateInnerBigDec\":10,\"crate_inner_str\":\"Single inner\",\"date\":\"14.05.2015 || 11:10:01\"},\"crateInnerList\":[{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 0\"},{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 1\"}],\"date\":\"2015-05-14T11:10:01\"},\"secondBoxStr\":\"Second box string\"}"; - Box result = jsonb.fromJson(expected, Box.class); + Box result = jsonb.fromJson(expected, Box.class); - //deserialized by deserializationContext.deserialize(Class c) - assertEquals(box.crate.crateInner.crateInnerBigDec, result.crate.crateInner.crateInnerBigDec); - assertEquals(box.crate.crateInner.crateInnerStr, result.crate.crateInner.crateInnerStr); + //deserialized by deserializationContext.deserialize(Class c) + assertEquals(box.crate.crateInner.crateInnerBigDec, result.crate.crateInner.crateInnerBigDec); + assertEquals(box.crate.crateInner.crateInnerStr, result.crate.crateInner.crateInnerStr); - assertEquals("List inner 0", result.crate.crateInnerList.get(0).crateInnerStr); - assertEquals("List inner 1", result.crate.crateInnerList.get(1).crateInnerStr); + assertEquals("List inner 0", result.crate.crateInnerList.get(0).crateInnerStr); + assertEquals("List inner 1", result.crate.crateInnerList.get(1).crateInnerStr); - //set by deserializer statically - assertEquals(new BigDecimal("123"), result.crate.crateBigDec); - assertEquals("abc", result.crate.crateStr); + //set by deserializer statically + assertEquals(new BigDecimal("123"), result.crate.crateBigDec); + assertEquals("abc", result.crate.crateStr); + }); } /** @@ -186,36 +206,38 @@ public void testDeserializerDeserializationByType() { */ @Test public void testSerializerSerializationOfType() { - JsonbConfig config = new JsonbConfig().withSerializers(new CrateSerializer()); - Jsonb jsonb = JsonbBuilder.create(config); - String expected = "{\"boxStr\":\"Box string\",\"crate\":{\"crateStr\":\"REPLACED crate str\",\"crateInner\":{\"crateInnerBigDec\":10,\"crate_inner_str\":\"Single inner\"},\"crateInnerList\":[{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 0\"},{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 1\"}],\"crateBigDec\":54321},\"secondBoxStr\":\"Second box string\"}"; - Box pojo = createPojo(); - - assertEquals(expected, jsonb.toJson(pojo)); - - Box result = jsonb.fromJson(expected, Box.class); - assertEquals(new BigDecimal("54321"), result.crate.crateBigDec); - //result.crate.crateStr is mapped to crate_str by jsonb property - assertNull(result.crate.crateStr); - assertEquals(pojo.crate.crateInner.crateInnerStr, result.crate.crateInner.crateInnerStr); - assertEquals(pojo.crate.crateInner.crateInnerBigDec, result.crate.crateInner.crateInnerBigDec); + testWithJsonbBuilderCreate(new JsonbConfig().withSerializers(new CrateSerializer()), jsonb -> { + String expected = + "{\"boxStr\":\"Box string\",\"crate\":{\"crateStr\":\"REPLACED crate str\",\"crateInner\":{\"crateInnerBigDec\":10,\"crate_inner_str\":\"Single inner\"},\"crateInnerList\":[{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 0\"},{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 1\"}],\"crateBigDec\":54321},\"secondBoxStr\":\"Second box string\"}"; + Box pojo = createPojo(); + + assertEquals(expected, jsonb.toJson(pojo)); + + Box result = jsonb.fromJson(expected, Box.class); + assertEquals(new BigDecimal("54321"), result.crate.crateBigDec); + //result.crate.crateStr is mapped to crate_str by jsonb property + assertNull(result.crate.crateStr); + assertEquals(pojo.crate.crateInner.crateInnerStr, result.crate.crateInner.crateInnerStr); + assertEquals(pojo.crate.crateInner.crateInnerBigDec, result.crate.crateInner.crateInnerBigDec); + }); } @Test public void testSerializerSerializationOfTypeWithExplicitType() { - JsonbConfig config = new JsonbConfig().withSerializers(new CrateSerializer()); - Jsonb jsonb = JsonbBuilder.create(config); - String expected = "{\"boxStr\":\"Box string\",\"crate\":{\"crateStr\":\"REPLACED crate str\",\"crateInner\":{\"crateInnerBigDec\":10,\"crate_inner_str\":\"Single inner\"},\"crateInnerList\":[{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 0\"},{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 1\"}],\"crateBigDec\":54321},\"secondBoxStr\":\"Second box string\"}"; - Box pojo = createPojo(); - - assertEquals(expected, jsonb.toJson(pojo, Box.class)); - - Box result = jsonb.fromJson(expected, Box.class); - assertEquals(new BigDecimal("54321"), result.crate.crateBigDec); - //result.crate.crateStr is mapped to crate_str by jsonb property - assertNull(result.crate.crateStr); - assertEquals(pojo.crate.crateInner.crateInnerStr, result.crate.crateInner.crateInnerStr); - assertEquals(pojo.crate.crateInner.crateInnerBigDec, result.crate.crateInner.crateInnerBigDec); + testWithJsonbBuilderCreate(new JsonbConfig().withSerializers(new CrateSerializer()), jsonb -> { + String expected = + "{\"boxStr\":\"Box string\",\"crate\":{\"crateStr\":\"REPLACED crate str\",\"crateInner\":{\"crateInnerBigDec\":10,\"crate_inner_str\":\"Single inner\"},\"crateInnerList\":[{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 0\"},{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 1\"}],\"crateBigDec\":54321},\"secondBoxStr\":\"Second box string\"}"; + Box pojo = createPojo(); + + assertEquals(expected, jsonb.toJson(pojo, Box.class)); + + Box result = jsonb.fromJson(expected, Box.class); + assertEquals(new BigDecimal("54321"), result.crate.crateBigDec); + //result.crate.crateStr is mapped to crate_str by jsonb property + assertNull(result.crate.crateStr); + assertEquals(pojo.crate.crateInner.crateInnerStr, result.crate.crateInner.crateInnerStr); + assertEquals(pojo.crate.crateInner.crateInnerBigDec, result.crate.crateInner.crateInnerBigDec); + }); } /** @@ -223,27 +245,29 @@ public void testSerializerSerializationOfTypeWithExplicitType() { */ @Test public void testDeserializersUsingConversion() { - JsonbConfig config = new JsonbConfig().withDeserializers(new CrateDeserializerWithConversion()); - Jsonb jsonb = JsonbBuilder.create(config); - - String json = "{\"boxStr\":\"Box string\",\"crate\":{\"date-converted\":\"2015-05-14T11:10:01\",\"crateStr\":\"REPLACED crate str\",\"crateInner\":{\"crateInnerBigDec\":10,\"crate_inner_str\":\"Single inner\",\"date\":\"14.05.2015 || 11:10:01\"},\"crateBigDec\":54321},\"secondBoxStr\":\"Second box string\"}"; - Box result = jsonb.fromJson(json, Box.class); - final Date expected = getExpectedDate(); - assertEquals(expected, result.crate.date); - assertEquals("Box string", result.boxStr); - assertEquals("Second box string", result.secondBoxStr); + testWithJsonbBuilderCreate(new JsonbConfig().withDeserializers(new CrateDeserializerWithConversion()), jsonb -> { + + String json = + "{\"boxStr\":\"Box string\",\"crate\":{\"date-converted\":\"2015-05-14T11:10:01\",\"crateStr\":\"REPLACED crate str\",\"crateInner\":{\"crateInnerBigDec\":10,\"crate_inner_str\":\"Single inner\",\"date\":\"14.05.2015 || 11:10:01\"},\"crateBigDec\":54321},\"secondBoxStr\":\"Second box string\"}"; + Box result = jsonb.fromJson(json, Box.class); + final Date expected = getExpectedDate(); + assertEquals(expected, result.crate.date); + assertEquals("Box string", result.boxStr); + assertEquals("Second box string", result.secondBoxStr); + }); } @Test public void testCrateJsonObjectDeserializer() { - JsonbConfig config = new JsonbConfig().withDeserializers(new CrateJsonObjectDeserializer()); - Jsonb jsonb = JsonbBuilder.create(config); - String expected = "{\"boxStr\":\"Box string\",\"crate\":{\"date-converted\":\"2015-05-14T11:10:01\",\"crateStr\":\"REPLACED crate str\",\"crateInner\":{\"crateInnerBigDec\":10,\"crateInnerStr\":\"Single inner\"},\"crateInnerList\":[{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 0\"},{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 1\"}],\"crateBigDec\":54321},\"secondBoxStr\":\"Second box string\"}"; - Box result = jsonb.fromJson(expected, Box.class); - assertEquals(new BigDecimal("54321"), result.crate.crateBigDec); - assertEquals("REPLACED crate str", result.crate.crateStr); - assertEquals("Single inner", result.crate.crateInner.crateInnerStr); - assertEquals(BigDecimal.TEN, result.crate.crateInner.crateInnerBigDec); + testWithJsonbBuilderCreate(new JsonbConfig().withDeserializers(new CrateJsonObjectDeserializer()), jsonb -> { + String expected = + "{\"boxStr\":\"Box string\",\"crate\":{\"date-converted\":\"2015-05-14T11:10:01\",\"crateStr\":\"REPLACED crate str\",\"crateInner\":{\"crateInnerBigDec\":10,\"crateInnerStr\":\"Single inner\"},\"crateInnerList\":[{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 0\"},{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 1\"}],\"crateBigDec\":54321},\"secondBoxStr\":\"Second box string\"}"; + Box result = jsonb.fromJson(expected, Box.class); + assertEquals(new BigDecimal("54321"), result.crate.crateBigDec); + assertEquals("REPLACED crate str", result.crate.crateStr); + assertEquals("Single inner", result.crate.crateInner.crateInnerStr); + assertEquals(BigDecimal.TEN, result.crate.crateInner.crateInnerBigDec); + }); } private static Date getExpectedDate() { @@ -252,11 +276,12 @@ private static Date getExpectedDate() { @Test public void testSerializationUsingConversion() { - JsonbConfig config = new JsonbConfig().withSerializers(new CrateSerializerWithConversion()); - Jsonb jsonb = JsonbBuilder.create(config); + testWithJsonbBuilderCreate(new JsonbConfig().withSerializers(new CrateSerializerWithConversion()), jsonb -> { - String json = "{\"boxStr\":\"Box string\",\"crate\":{\"crateStr\":\"REPLACED crate str\",\"crateInner\":{\"crateInnerBigDec\":10,\"crate_inner_str\":\"Single inner\",\"date\":\"14.05.2015 || 11:10:01\"},\"crateInnerList\":[{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 0\"},{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 1\"}],\"crateBigDec\":54321,\"date-converted\":\"2015-05-14T11:10:01Z[UTC]\"},\"secondBoxStr\":\"Second box string\"}"; - assertEquals(json, jsonb.toJson(createPojoWithDates())); + String json = + "{\"boxStr\":\"Box string\",\"crate\":{\"crateStr\":\"REPLACED crate str\",\"crateInner\":{\"crateInnerBigDec\":10,\"crate_inner_str\":\"Single inner\",\"date\":\"14.05.2015 || 11:10:01\"},\"crateInnerList\":[{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 0\"},{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 1\"}],\"crateBigDec\":54321,\"date-converted\":\"2015-05-14T11:10:01Z[UTC]\"},\"secondBoxStr\":\"Second box string\"}"; + assertEquals(json, jsonb.toJson(createPojoWithDates())); + }); } @Test @@ -293,37 +318,38 @@ public void testAnnotations() { @Test public void testAnnotationsOverride() { - JsonbConfig config = new JsonbConfig().withDeserializers(new CrateJsonObjectDeserializer()).withSerializers(new CrateSerializer()); - Jsonb jsonb = JsonbBuilder.create(config); + testWithJsonbBuilderCreate(new JsonbConfig().withDeserializers(new CrateJsonObjectDeserializer()).withSerializers(new CrateSerializer()), jsonb -> { - BoxWithAnnotations box = new BoxWithAnnotations(); - box.boxStr = "Box string"; - box.secondBoxStr = "Second box string"; - box.crate = new Crate(); - box.crate.crateInner = createCrateInner("Single inner"); - box.crate.date = getExpectedDate(); + BoxWithAnnotations box = new BoxWithAnnotations(); + box.boxStr = "Box string"; + box.secondBoxStr = "Second box string"; + box.crate = new Crate(); + box.crate.crateInner = createCrateInner("Single inner"); + box.crate.date = getExpectedDate(); - box.crate.crateInnerList = new ArrayList<>(); - box.crate.crateInnerList.add(createCrateInner("List inner 0")); - box.crate.crateInnerList.add(createCrateInner("List inner 1")); + box.crate.crateInnerList = new ArrayList<>(); + box.crate.crateInnerList.add(createCrateInner("List inner 0")); + box.crate.crateInnerList.add(createCrateInner("List inner 1")); - String expected = "{\"boxStr\":\"Box string\",\"crate\":{\"crateStr\":\"REPLACED crate str\",\"crateInner\":{\"crateInnerBigDec\":10,\"crate_inner_str\":\"Single inner\"},\"crateInnerList\":[{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 0\"},{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 1\"}],\"crateBigDec\":54321,\"date-converted\":\"2015-05-14T11:10:01Z[UTC]\"},\"secondBoxStr\":\"Second box string\"}"; + String expected = + "{\"boxStr\":\"Box string\",\"crate\":{\"crateStr\":\"REPLACED crate str\",\"crateInner\":{\"crateInnerBigDec\":10,\"crate_inner_str\":\"Single inner\"},\"crateInnerList\":[{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 0\"},{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 1\"}],\"crateBigDec\":54321,\"date-converted\":\"2015-05-14T11:10:01Z[UTC]\"},\"secondBoxStr\":\"Second box string\"}"; - assertEquals(expected, jsonb.toJson(box)); + assertEquals(expected, jsonb.toJson(box)); - BoxWithAnnotations result = jsonb.fromJson(expected, BoxWithAnnotations.class); + BoxWithAnnotations result = jsonb.fromJson(expected, BoxWithAnnotations.class); - //deserialized by deserializationContext.deserialize(Class c) - assertEquals(box.crate.crateInner.crateInnerBigDec, result.crate.crateInner.crateInnerBigDec); - assertEquals(box.crate.crateInner.crateInnerStr, result.crate.crateInner.crateInnerStr); + //deserialized by deserializationContext.deserialize(Class c) + assertEquals(box.crate.crateInner.crateInnerBigDec, result.crate.crateInner.crateInnerBigDec); + assertEquals(box.crate.crateInner.crateInnerStr, result.crate.crateInner.crateInnerStr); - assertEquals(2L, result.crate.crateInnerList.size()); - assertEquals("List inner 0", result.crate.crateInnerList.get(0).crateInnerStr); - assertEquals("List inner 1", result.crate.crateInnerList.get(1).crateInnerStr); + assertEquals(2L, result.crate.crateInnerList.size()); + assertEquals("List inner 0", result.crate.crateInnerList.get(0).crateInnerStr); + assertEquals("List inner 1", result.crate.crateInnerList.get(1).crateInnerStr); - //set by deserializer statically - assertEquals(new BigDecimal("123"), result.crate.crateBigDec); - assertEquals("abc", result.crate.crateStr); + //set by deserializer statically + assertEquals(new BigDecimal("123"), result.crate.crateBigDec); + assertEquals("abc", result.crate.crateStr); + }); } @Test @@ -364,21 +390,22 @@ public void testContainerSerializer() { */ @Test public void testRecursiveSerializer() { - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withSerializers(new RecursiveSerializer()).withDeserializers(new RecursiveDeserializer())); - - Box box = new Box(); - box.boxStr = "Box to serialize"; - try { - jsonb.toJson(box); - fail(); - } catch (JsonbException ex) { - } + testWithJsonbBuilderCreate(new JsonbConfig().withSerializers(new RecursiveSerializer()).withDeserializers(new RecursiveDeserializer()), jsonb -> { + + Box box = new Box(); + box.boxStr = "Box to serialize"; + try { + jsonb.toJson(box); + fail(); + } catch (JsonbException ignored) { + } - try { - jsonb.fromJson("{\"boxStr\":\"Box to deserialize\"}", Box.class); - fail(); - } catch (StackOverflowError error){ - } + try { + jsonb.fromJson("{\"boxStr\":\"Box to deserialize\"}", Box.class); + fail(); + } catch (StackOverflowError ignored) { + } + }); } @Test @@ -394,76 +421,105 @@ public void testAuthor() { assertEquals("Connor", result.getLastName()); } + @Test + public void testSupertypeSerializer_withConfiguration() { + NumberSerializer.getCounter().resetCount(); + NumberDeserializer.getCounter().resetCount(); + testWithJsonbBuilderCreate(new JsonbConfig() + .withSerializers(new NumberSerializer()) + .withDeserializers(new NumberDeserializer()), jsonb -> { + SupertypeSerializerPojo pojo = new SupertypeSerializerPojo(); + pojo.setNumberInteger(10); + pojo.setAnotherNumberInteger(11); + assertEquals("{\"anotherNumberInteger\":\"12\",\"numberInteger\":\"11\"}", jsonb.toJson(pojo)); + + pojo = jsonb.fromJson("{\"anotherNumberInteger\":\"12\",\"numberInteger\":\"11\"}", SupertypeSerializerPojo.class); + assertEquals(Integer.valueOf(10), pojo.getNumberInteger()); + assertEquals(Integer.valueOf(11), pojo.getAnotherNumberInteger()); + //assert that deserializer and serializer were reused + assertEquals(1, NumberSerializer.getCounter().getCount()); + assertEquals(1, NumberDeserializer.getCounter().getCount()); + }); + } + @Test public void testSupertypeSerializer() { - Jsonb jsonb = JsonbBuilder.create( - new JsonbConfig().withSerializers(new NumberSerializer()) - .withDeserializers(new NumberDeserializer())); + NumberSerializer.getCounter().resetCount(); + NumberDeserializer.getCounter().resetCount(); SupertypeSerializerPojo pojo = new SupertypeSerializerPojo(); - pojo.setNumberInteger(10); + pojo.setNumberInteger(9); pojo.setAnotherNumberInteger(11); - assertEquals("{\"anotherNumberInteger\":\"12\",\"numberInteger\":\"11\"}", jsonb.toJson(pojo)); + assertEquals("{\"anotherNumberInteger\":11,\"numberInteger\":\"10\"}", defaultJsonb.toJson(pojo)); - pojo = jsonb.fromJson("{\"anotherNumberInteger\":\"12\",\"numberInteger\":\"11\"}", SupertypeSerializerPojo.class); - assertEquals(Integer.valueOf(10), pojo.getNumberInteger()); + pojo = defaultJsonb.fromJson("{\"anotherNumberInteger\":11,\"numberInteger\":\"10\"}", SupertypeSerializerPojo.class); + assertEquals(Integer.valueOf(9), pojo.getNumberInteger()); assertEquals(Integer.valueOf(11), pojo.getAnotherNumberInteger()); + //assert that deserializer and serializer were used just once + assertEquals(1, NumberSerializer.getCounter().getCount()); + assertEquals(1, NumberDeserializer.getCounter().getCount()); } - + @Test public void testObjectDeserializerWithLexOrderStrategy() { - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.LEXICOGRAPHICAL)); - Object pojo = jsonb.fromJson("{\"first\":{},\"third\":{},\"second\":{\"second\":2,\"first\":1}}", Object.class); - assertTrue(pojo instanceof TreeMap, "Pojo is not of type TreeMap"); - @SuppressWarnings("unchecked") - SortedMap pojoAsMap = (SortedMap) pojo; - assertTrue(pojoAsMap.get("second") instanceof TreeMap, "Pojo inner object is not of type TreeMap"); - assertEquals("{\"first\":{},\"second\":{\"first\":1,\"second\":2},\"third\":{}}", jsonb.toJson(pojo)); + testWithJsonbBuilderCreate(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.LEXICOGRAPHICAL), jsonb -> { + Object pojo = jsonb.fromJson("{\"first\":{},\"third\":{},\"second\":{\"second\":2,\"first\":1}}", Object.class); + assertInstanceOf(TreeMap.class, pojo, "Pojo is not of type TreeMap"); + @SuppressWarnings("unchecked") + SortedMap pojoAsMap = (SortedMap) pojo; + assertInstanceOf(TreeMap.class, pojoAsMap.get("second"), "Pojo inner object is not of type TreeMap"); + assertEquals("{\"first\":{},\"second\":{\"first\":1,\"second\":2},\"third\":{}}", jsonb.toJson(pojo)); + }); } @Test public void testObjectDeserializerWithReverseOrderStrategy() { - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.REVERSE)); - Object pojo = jsonb.fromJson("{\"first\":{},\"second\":{\"first\":1,\"second\":2},\"third\":{}}", Object.class); - assertTrue(pojo instanceof ReverseTreeMap, "Pojo is not of type ReverseTreeMap"); - @SuppressWarnings("unchecked") - SortedMap pojoAsMap = (SortedMap) pojo; - assertTrue(pojoAsMap.get("second") instanceof TreeMap, "Pojo inner object is not of type TreeMap"); - assertEquals("{\"third\":{},\"second\":{\"second\":2,\"first\":1},\"first\":{}}", jsonb.toJson(pojo)); + testWithJsonbBuilderCreate(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.REVERSE), jsonb -> { + Object pojo = jsonb.fromJson("{\"first\":{},\"second\":{\"first\":1,\"second\":2},\"third\":{}}", Object.class); + assertInstanceOf(ReverseTreeMap.class, pojo, "Pojo is not of type ReverseTreeMap"); + @SuppressWarnings("unchecked") + SortedMap pojoAsMap = (SortedMap) pojo; + assertInstanceOf(TreeMap.class, pojoAsMap.get("second"), "Pojo inner object is not of type TreeMap"); + assertEquals("{\"third\":{},\"second\":{\"second\":2,\"first\":1},\"first\":{}}", jsonb.toJson(pojo)); + }); } @Test public void testObjectDeserializerWithAnyOrNoneOrderStrategy() { String json = "{\"first\":{},\"second\":{\"first\":1,\"second\":2},\"third\":{}}"; // ANY - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.ANY)); - Object pojo = jsonb.fromJson(json, Object.class); - assertTrue(pojo instanceof HashMap, "Pojo is not of type HashMap with \"ANY\" strategy"); - // none - pojo = defaultJsonb.fromJson(json, Object.class); - assertTrue(pojo instanceof HashMap, "Pojo is not of type HashMap with no strategy"); + testWithJsonbBuilderCreate(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.ANY), jsonb -> { + Object pojo = jsonb.fromJson(json, Object.class); + assertInstanceOf(HashMap.class, pojo, "Pojo is not of type HashMap with \"ANY\" strategy"); + // none + pojo = defaultJsonb.fromJson(json, Object.class); + assertInstanceOf(HashMap.class, pojo, "Pojo is not of type HashMap with no strategy"); + }); } @Test - public void testSortedMapDerializer() { + public void testSortedMapDeserializer() { String json = "{\"first\":1,\"third\":3,\"second\":2}"; - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.ANY)); - SortedMap pojo = jsonb.fromJson(json, SortedMap.class); - assertTrue(pojo instanceof TreeMap, "Pojo is not of type TreeMap with \"ANY\" strategy"); - - jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.LEXICOGRAPHICAL)); - pojo = jsonb.fromJson(json, SortedMap.class); - assertTrue(pojo instanceof TreeMap, "Pojo is not of type TreeMap with no strategy"); - assertEquals("{\"first\":1,\"second\":2,\"third\":3}", jsonb.toJson(pojo)); - - jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.REVERSE)); - pojo = jsonb.fromJson(json, SortedMap.class); - assertTrue(pojo instanceof ReverseTreeMap, "Pojo is not of type ReverseTreeMap with no strategy"); - assertEquals("{\"third\":3,\"second\":2,\"first\":1}", jsonb.toJson(pojo)); - - pojo = defaultJsonb.fromJson(json, SortedMap.class); - assertTrue(pojo instanceof TreeMap, "Pojo is not of type TreeMap with no strategy"); - assertEquals("{\"first\":1,\"second\":2,\"third\":3}", defaultJsonb.toJson(pojo)); + testWithJsonbBuilderCreate(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.ANY), jsonb -> { + SortedMap pojo = jsonb.fromJson(json, SortedMap.class); + assertInstanceOf(TreeMap.class, pojo, "Pojo is not of type TreeMap with \"ANY\" strategy"); + }); + + testWithJsonbBuilderCreate(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.LEXICOGRAPHICAL), jsonb -> { + SortedMap pojo = jsonb.fromJson(json, SortedMap.class); + assertInstanceOf(TreeMap.class, pojo, "Pojo is not of type TreeMap with no strategy"); + assertEquals("{\"first\":1,\"second\":2,\"third\":3}", jsonb.toJson(pojo)); + }); + + testWithJsonbBuilderCreate(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.REVERSE), jsonb -> { + SortedMap pojo = jsonb.fromJson(json, SortedMap.class); + assertInstanceOf(ReverseTreeMap.class, pojo, "Pojo is not of type ReverseTreeMap with no strategy"); + assertEquals("{\"third\":3,\"second\":2,\"first\":1}", jsonb.toJson(pojo)); + + pojo = defaultJsonb.fromJson(json, SortedMap.class); + assertInstanceOf(TreeMap.class, pojo, "Pojo is not of type TreeMap with no strategy"); + assertEquals("{\"first\":1,\"second\":2,\"third\":3}", defaultJsonb.toJson(pojo)); + }); } @Test @@ -491,12 +547,13 @@ public void testSerializeMapWithNulls() { @Test public void testSerializeMapWithNullsForceArraySerializer() { - Jsonb jsonb = JsonbBuilder.create(new YassonConfig() + testWithJsonbBuilderCreate(new YassonConfig() .withForceMapArraySerializerForNullKeys(Boolean.TRUE) - .withNullValues(Boolean.TRUE)); - assertEquals("[{\"key\":null,\"value\":null}]", jsonb.toJson(singletonMap(null, null))); - assertEquals("{\"key\":null}", jsonb.toJson(singletonMap("key", null))); - assertEquals("[{\"key\":null,\"value\":\"value\"}]", jsonb.toJson(singletonMap(null, "value"))); + .withNullValues(Boolean.TRUE), jsonb -> { + assertEquals("[{\"key\":null,\"value\":null}]", jsonb.toJson(singletonMap(null, null))); + assertEquals("{\"key\":null}", jsonb.toJson(singletonMap("key", null))); + assertEquals("[{\"key\":null,\"value\":\"value\"}]", jsonb.toJson(singletonMap(null, "value"))); + }); } /** @@ -506,26 +563,27 @@ public void testSerializeMapWithNullsForceArraySerializer() { @Test public void testSerializeMapToJsonObject() { Map map = new HashMap<>(); - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()); map.put("name", "John SMith"); map.put("age", 35); map.put("married", true); - String json = jsonb.toJson(map); + String json = defaultJsonb.toJson(map); JsonObject jobj = Json.createReader(new StringReader(json)).read().asJsonObject(); assertEquals("John SMith", jobj.getString("name")); assertEquals(35, jobj.getInt("age")); - assertEquals(true, jobj.getBoolean("married")); + assertTrue(jobj.getBoolean("married")); } @Test public void testDeserializeArrayWithAdvancingParserAfterObjectEnd() { String json = "[{\"stringProperty\":\"Property 1 value\"},{\"stringProperty\":\"Property 2 value\"}]"; - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withDeserializers(new SimplePojoDeserializer())); - SimplePojo[] result = jsonb.fromJson(json, SimplePojo[].class); - assertEquals(2, result.length); + testWithJsonbBuilderCreate(new JsonbConfig().withDeserializers(new SimplePojoDeserializer()), jsonb -> { + SimplePojo[] result = jsonb.fromJson(json, SimplePojo[].class); + assertEquals(2, result.length); + assertEquals("Property 1 value", result[0].getStringProperty()); + }); } - public class SimplePojoDeserializer implements JsonbDeserializer { + public static class SimplePojoDeserializer implements JsonbDeserializer { @Override public SimplePojo deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { //parser.getObject advances the parser to END_OBJECT. @@ -539,12 +597,14 @@ public SimplePojo deserialize(JsonParser parser, DeserializationContext ctx, Typ @Test public void testDeserializeArrayWithAdvancingParserAfterObjectEndUsingValue() { String json = "[{\"stringProperty\":\"Property 1 value\"},{\"stringProperty\":\"Property 2 value\"}]"; - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withDeserializers(new SimplePojoValueDeserializer())); - SimplePojo[] result = jsonb.fromJson(json, SimplePojo[].class); - assertEquals(2, result.length); + testWithJsonbBuilderCreate(new JsonbConfig().withDeserializers(new SimplePojoValueDeserializer()), jsonb -> { + SimplePojo[] result = jsonb.fromJson(json, SimplePojo[].class); + assertEquals(2, result.length); + assertEquals("Property 1 value", result[0].getStringProperty()); + }); } - public class SimplePojoValueDeserializer implements JsonbDeserializer { + public static class SimplePojoValueDeserializer implements JsonbDeserializer { @Override public SimplePojo deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { //parser.getValue advances the parser to END_OBJECT in case of object. @@ -555,7 +615,7 @@ public SimplePojo deserialize(JsonParser parser, DeserializationContext ctx, Typ } } - public class SimplePojo { + public static class SimplePojo { private String stringProperty; public String getStringProperty() { @@ -617,26 +677,27 @@ public void serialize(Baz obj, JsonGenerator generator, SerializationContext ctx } /** - * Test for issue: https://github.com/quarkusio/quarkus/issues/8925 + * Test for issue: issue 8925 * Ensure that if multiple customizations (serializer, deserializer, or adapter) are applied * for different types in the same class hierarchy, we use the customization for the most * specific type in the class hierarchy. */ @Test public void testSerializerMatching() { - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig() - .withSerializers(new FooSerializer(), new BazSerializer())); - assertEquals("\"foo\"", jsonb.toJson(new Foo())); - // Since 'Bar' does not have its own serializer, it should use - // the next serializer in the tree (FooSerializer) - assertEquals("\"foo\"", jsonb.toJson(new Bar())); - assertEquals("\"baz\"", jsonb.toJson(new Baz())); + testWithJsonbBuilderCreate(new JsonbConfig() + .withSerializers(new FooSerializer(), new BazSerializer()), jsonb -> { + assertEquals("\"foo\"", jsonb.toJson(new Foo())); + // Since 'Bar' does not have its own serializer, it should use + // the next serializer in the tree (FooSerializer) + assertEquals("\"foo\"", jsonb.toJson(new Bar())); + assertEquals("\"baz\"", jsonb.toJson(new Baz())); + }); } - public static interface One { } - public static interface Two { } + public interface One { } + public interface Two { } - public static interface Three { } + public interface Three { } public static class OneTwo implements One, Two { } public static class OneTwoThree implements One, Two, Three { } @@ -661,21 +722,23 @@ public void serialize(Three obj, JsonGenerator generator, SerializationContext c @Test public void testSerializerMatchingInterfaces01() { - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig() - .withSerializers(new OneSerializer(), new TwoSerializer(), new ThreeSerializer())); - assertEquals("\"one\"", jsonb.toJson(new OneTwo())); - assertEquals("\"one\"", jsonb.toJson(new OneTwoThree())); + testWithJsonbBuilderCreate(new JsonbConfig() + .withSerializers(new OneSerializer(), new TwoSerializer(), new ThreeSerializer()), jsonb -> { + assertEquals("\"one\"", jsonb.toJson(new OneTwo())); + assertEquals("\"one\"", jsonb.toJson(new OneTwoThree())); + }); } @Test public void testSerializerMatchingInterfaces02() { - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig() - .withSerializers(new ThreeSerializer(), new TwoSerializer())); - assertEquals("\"two\"", jsonb.toJson(new OneTwo())); - assertEquals("\"two\"", jsonb.toJson(new OneTwoThree())); + testWithJsonbBuilderCreate(new JsonbConfig() + .withSerializers(new ThreeSerializer(), new TwoSerializer()), jsonb -> { + assertEquals("\"two\"", jsonb.toJson(new OneTwo())); + assertEquals("\"two\"", jsonb.toJson(new OneTwoThree())); + }); } - public class GenericBean { + public static class GenericBean { public T value; @@ -687,9 +750,13 @@ public boolean equals(Object obj) { return Boolean.FALSE; } + @Override + public int hashCode() { + return Objects.hash(value); + } } - public class GenericBeanSerializer implements JsonbSerializer { + public static class GenericBeanSerializer implements JsonbSerializer { private Boolean called = Boolean.FALSE; @@ -702,7 +769,7 @@ public void serialize(GenericBean t, JsonGenerator jg, SerializationContext sc) } } - public class GenericBeanDeserializer implements JsonbDeserializer { + public static class GenericBeanDeserializer implements JsonbDeserializer { private Boolean called = Boolean.FALSE; @@ -722,94 +789,313 @@ public void testCustomDeserializerWithParameterizedType() { GenericBeanSerializer genericBeanSerializer = new GenericBeanSerializer(); GenericBeanDeserializer genericBeanDeserializer = new GenericBeanDeserializer(); - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withDeserializers(genericBeanDeserializer).withSerializers(genericBeanSerializer)); + testWithJsonbBuilderCreate(new JsonbConfig().withDeserializers(genericBeanDeserializer).withSerializers(genericBeanSerializer), jsonb -> { + + GenericBean bean1 = new GenericBean<>(); + bean1.value = "test1"; + GenericBean bean2 = new GenericBean<>(); + bean2.value = "test2"; + GenericBean bean3 = new GenericBean<>(); + bean3.value = "test3"; + + Collection> asList = Arrays.asList(bean1, bean2, bean3); + + String toJson = jsonb.toJson(asList); + + assertEquals(toJson, "[{\"value\":\"test1\"},{\"value\":\"test2\"},{\"value\":\"test3\"}]"); + assertTrue(genericBeanSerializer.called); + + List> fromJson = jsonb.fromJson( + toJson, + new ParameterizedType() { + @Override + public Type[] getActualTypeArguments() { + return new Type[] {GenericBean.class}; + } + + @Override + public Type getRawType() { + return Collection.class; + } + + @Override + public Type getOwnerType() { + return null; + } + } + ); + + assertEquals(asList, fromJson); + assertTrue(genericBeanDeserializer.called); + }); + } - GenericBean bean1 = new GenericBean<>(); - bean1.value = "test1"; - GenericBean bean2 = new GenericBean<>(); - bean2.value = "test2"; - GenericBean bean3 = new GenericBean<>(); - bean3.value = "test3"; + @Test + public void testImplicitJsonbSerializers() { + String expected = "{\"value\":\"123\"}"; + Box box = new Box(); + box.boxStr = "Box"; - Collection> asList = Arrays.asList(bean1, bean2, bean3); + testWithJsonbBuilderCreate(new JsonbConfig().withSerializers(new ExplicitJsonbSerializer()), jsonb -> assertEquals(expected, jsonb.toJson(box))); - String toJson = jsonb.toJson(asList); + testWithJsonbBuilderCreate(new JsonbConfig().withSerializers(new ImplicitJsonbSerializer()), jsonb -> assertEquals(expected, jsonb.toJson(box))); + } - assertEquals(toJson, "[{\"value\":\"test1\"},{\"value\":\"test2\"},{\"value\":\"test3\"}]"); - assertTrue(genericBeanSerializer.called); + @Test + public void testBoxToArrayChained() { + testWithJsonbBuilderCreate(new JsonbConfig().withSerializers(BOX_ARRAY_SERIALIZER_CHAINED), jsonb -> { + Box box = new Box(); + box.boxStr = "str1"; + box.secondBoxStr = "str2"; + String expected = "[\"str1\",\"str2\"]"; + + assertThat(jsonb.toJson(box), is(expected)); + }); + } - List> fromJson = jsonb.fromJson( - toJson, - new ParameterizedType() { - @Override - public Type[] getActualTypeArguments() { - return new Type[]{GenericBean.class}; - } + @Test + public void testBoxToArray() { + testWithJsonbBuilderCreate(new JsonbConfig().withSerializers(BOX_ARRAY_SERIALIZER), jsonb -> { + Box box = new Box(); + box.boxStr = "str1"; + box.secondBoxStr = "str2"; + String expected = "[\"str1\",\"str2\"]"; + + assertThat(jsonb.toJson(box), is(expected)); + }); + } - @Override - public Type getRawType() { - return Collection.class; - } + @Test + public void testBoxToArrayChainedWithLambda() { + RuntimeException runtimeException = assertThrows(RuntimeException.class, () -> testWithJsonbBuilderCreate(new JsonbConfig().withSerializers(BOX_ARRAY_SERIALIZER_CHAINED_AS_LAMBDA), jsonb -> { + Box box = new Box(); + box.boxStr = "str1"; + box.secondBoxStr = "str2"; + String expected = "[\"str1\",\"str2\"]"; - @Override - public Type getOwnerType() { - return null; - } - } - ); + assertThat(jsonb.toJson(box), is(expected)); + })); - assertEquals(asList, fromJson); - assertTrue(genericBeanDeserializer.called); + assertInstanceOf(JsonbException.class, runtimeException.getCause()); + } + + @Test + public void testBoxToArrayWithLambda() { + RuntimeException runtimeException = assertThrows(RuntimeException.class, () -> testWithJsonbBuilderCreate(new JsonbConfig().withSerializers(BOX_ARRAY_SERIALIZER_AS_LAMBDA), jsonb -> { + Box box = new Box(); + box.boxStr = "str1"; + box.secondBoxStr = "str2"; + String expected = "[\"str1\",\"str2\"]"; + + assertThat(jsonb.toJson(box), is(expected)); + })); + assertInstanceOf(JsonbException.class, runtimeException.getCause()); } @Test - public void testImplicitJsonbSerializers() { - Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withSerializers(new ExplicitJsonbSerializer())); - String expected = "{\"value\":\"123\"}"; - Box box = new Box(); - box.boxStr = "Box"; - assertEquals(expected, jsonb.toJson(box)); + public void testCustomSerializersInContainer() { + Container expected = new Container(List.of(new Containee("k", "v"))); + + String expectedJson = defaultJsonb.toJson(expected); + System.out.println(expectedJson); - jsonb = JsonbBuilder.create(new JsonbConfig().withSerializers(new ImplicitJsonbSerializer())); - assertEquals(expected, jsonb.toJson(box)); + assertEquals(expected, defaultJsonb.fromJson(expectedJson, Container.class)); } @Test - public void testBoxToArrayChained() { - JsonbConfig cfg = new JsonbConfig().withSerializers(BOX_ARRAY_SERIALIZER_CHAINED); - Jsonb jsonb = JsonbBuilder.create(cfg); - Box box = new Box(); - box.boxStr = "str1"; - box.secondBoxStr = "str2"; - String expected = "[\"str1\",\"str2\"]"; + public void testCustomSerializersAndDeserializersInEnum() { + Colors expected = Colors.GREEN; + + String expectedJson = defaultJsonb.toJson(expected); - assertThat(jsonb.toJson(box), is(expected)); + assertEquals(expected, defaultJsonb.fromJson(expectedJson, Colors.class)); } @Test - public void testBoxToArray() { - JsonbConfig cfg = new JsonbConfig().withSerializers(BOX_ARRAY_SERIALIZER); - Jsonb jsonb = JsonbBuilder.create(cfg); - Box box = new Box(); - box.boxStr = "str1"; - box.secondBoxStr = "str2"; - String expected = "[\"str1\",\"str2\"]"; + public void testJsonbPropertyInEnum() { + Cars expected = Cars.FORD; - assertThat(jsonb.toJson(box), is(expected)); + String expectedJson = defaultJsonb.toJson(expected); + + assertEquals(expected, defaultJsonb.fromJson(expectedJson, Cars.class)); } @Test - public void testCustomSerializersInContainer(){ - Jsonb jsonb = JsonbBuilder.create(); + public void testNoJsonbPropertyInEnum() { + Cars expected = Cars.FIAT; - Container expected = new Container(List.of(new Containee("k", "v"))); + String expectedJson = defaultJsonb.toJson(expected); - String expectedJson = jsonb.toJson(expected); - System.out.println(expectedJson); + assertEquals(expected, defaultJsonb.fromJson(expectedJson, Cars.class)); + } - assertEquals(expected, jsonb.fromJson(expectedJson, Container.class)); + @Test + void testWildcardType_inGeneric() { + Wrapper pojo = new Wrapper(new Wrapped<>("first")); + assertEquals("{\"value\":{\"value\":\"first\"}}", defaultJsonb.toJson(pojo)); + } + + @Test + public void testClass_withGenericField_whichIsGenericType() { + Wrapper pojo = new Wrapper(new Wrapped<>(new Wrapped<>("one"))); + assertEquals("{\"value\":{\"value\":{\"value\":\"one\"}}}", defaultJsonb.toJson(pojo)); + testWithJsonbBuilderCreate(new JsonbConfig().withSerializers(new JsonbSerializer() { + @Override public void serialize(Wrapped obj, JsonGenerator generator, SerializationContext ctx) { + generator.writeStartObject(); + ctx.serialize("value_ser", obj.value, generator); + generator.writeEnd(); + } + }), jsonb -> { + assertEquals("{\"value\":{\"value\":{\"value\":\"one\"}}}", jsonb.toJson(pojo)); + }); + } + + public static class Wrapper { + // we need a field of this type for the test, otherwise the constellation is not valid for the test + public Wrapped value; + + public Wrapper(Wrapped value) { + this.value = value; + } + } + + public static class Wrapped { + public T value; + // we need a constructor for the test so we could create an instance without explicit generic type specification + public Wrapped(T value) { + this.value = value; + } } -} + @Nested + class YassonParserTests{ + @Test + public void testJsonParserFunctions() { + JsonParserTestPojo expected = new JsonParserTestPojo().init(); + + JsonParserTestDeserializers.JsonParserTestObjectDeserializer + deserializer = new JsonParserTestDeserializers.JsonParserTestObjectDeserializer(); + testWithJsonbBuilderCreate(new JsonbConfig().withDeserializers(deserializer), jsonb -> { + String expectedJson = new StringBuilder(jsonb.toJson(expected)) + .insert(1, "\"stringList_skip\":[\"string7\",\"string8\"],\"subPojo_skip\":{\"name\":\"subPojo_skip\"},").toString(); + + assertTrue(TwoObjectsComparer.getDifferentFieldInTwoObjects(expected, jsonb.fromJson(expectedJson, JsonParserTestPojo.class)) + .isEmpty()); + assertEquals(List.of("stringList_skip", "subPojo_skip", "bigDecimal", "integer", "longValue", "string", "stringList", + "stringList_getStream", "stringList_getValue", "string_getValue", "subPojo", "subPojo_getStream", "subPojo_getValue"), + deserializer.getKeyNames()); + + List expectedListOfEvents = List.of(JsonParser.Event.START_OBJECT, JsonParser.Event.END_ARRAY, + JsonParser.Event.END_OBJECT, JsonParser.Event.VALUE_NUMBER, JsonParser.Event.VALUE_NUMBER, JsonParser.Event.VALUE_NUMBER, + JsonParser.Event.VALUE_STRING, JsonParser.Event.END_ARRAY, JsonParser.Event.END_ARRAY, JsonParser.Event.END_ARRAY, + JsonParser.Event.VALUE_STRING, JsonParser.Event.END_OBJECT, JsonParser.Event.END_OBJECT); + + assertEquals(expectedListOfEvents, deserializer.getParserEvents()); + assertEquals(expectedListOfEvents, deserializer.getContextEvents()); + assertEquals("(line no=1, column no=96, offset=95)", deserializer.getLocation()); + assertTrue(deserializer.isIntegralNumber()); + }); + } + + @Test + public void testJsonParserValueStream() { + JsonParserTestPojo expected = new JsonParserTestPojo().init(); + + JsonParserTestDeserializers.JsonParserTestValueStreamDeserializer + deserializer = new JsonParserTestDeserializers.JsonParserTestValueStreamDeserializer(); + testWithJsonbBuilderCreate(new JsonbConfig().withDeserializers(deserializer), jsonb -> { + String expectedJson = jsonb.toJson(expected); + + assertTrue(TwoObjectsComparer.getDifferentFieldInTwoObjects(expected, jsonb.fromJson(expectedJson, JsonParserTestPojo.class)) + .isEmpty()); + }); + } + + @Test + public void testJsonParser_NoSuchElementException() { + JsonParserTestDeserializers.JsonParserTestNoSuchElementExceptionDeserializer + deserializer = new JsonParserTestDeserializers.JsonParserTestNoSuchElementExceptionDeserializer(); + testWithJsonbBuilderCreate(new JsonbConfig().withDeserializers(deserializer), jsonb -> { + Throwable throwable = null; + try { + jsonb.fromJson("5", JsonParserTestPojo.class); + fail("NoSuchElementException should be thrown"); + } catch (JsonbException jbe) { + throwable = jbe.getCause(); + } + + assertInstanceOf(NoSuchElementException.class, throwable); + }); + } + + @Test + public void testJsonParser_GetObject_NotAtTheStartOfObject_IllegalStateException() { + JsonParserTestDeserializers.JsonParserTestGetObjectDeserializer + deserializer = new JsonParserTestDeserializers.JsonParserTestGetObjectDeserializer(); + testWithJsonbBuilderCreate(new JsonbConfig().withDeserializers(deserializer), jsonb -> { + Throwable throwable = null; + try { + jsonb.fromJson("5", JsonParserTestPojo.class); + fail("IllegalStateException should be thrown"); + } catch (JsonbException jbe) { + throwable = jbe.getCause(); + } + + assertInstanceOf(IllegalStateException.class, throwable); + }); + } + + @Test + public void testJsonParser_GetArray_NotAtTheStartOfArray_IllegalStateException() { + JsonParserTestDeserializers.JsonParserTestGetArrayDeserializer + deserializer = new JsonParserTestDeserializers.JsonParserTestGetArrayDeserializer(); + testWithJsonbBuilderCreate(new JsonbConfig().withDeserializers(deserializer), jsonb -> { + Throwable throwable = null; + try { + jsonb.fromJson("5", JsonParserTestPojo.class); + fail("IllegalStateException should be thrown"); + } catch (JsonbException jbe) { + throwable = jbe.getCause(); + } + + assertInstanceOf(IllegalStateException.class, throwable); + }); + } + + @Test + public void testJsonParser_GetValueEndOfObject_IllegalStateException() { + JsonParserTestDeserializers.JsonParserTestEndOfObjectDeserializer + deserializer = new JsonParserTestDeserializers.JsonParserTestEndOfObjectDeserializer(); + testWithJsonbBuilderCreate(new JsonbConfig().withDeserializers(deserializer), jsonb -> { + Throwable throwable = null; + try { + jsonb.fromJson("{\"a\":1}", JsonParserTestPojo.class); + fail("IllegalStateException should be thrown"); + } catch (JsonbException jbe) { + throwable = jbe.getCause(); + } + + assertInstanceOf(IllegalStateException.class, throwable); + }); + } + + @Test + public void testJsonParser_GetValueEndOfArray_IllegalStateException() { + JsonParserTestDeserializers.JsonParserTestEndOfArrayDeserializer + deserializer = new JsonParserTestDeserializers.JsonParserTestEndOfArrayDeserializer(); + testWithJsonbBuilderCreate(new JsonbConfig().withDeserializers(deserializer), jsonb -> { + Throwable throwable = null; + try { + jsonb.fromJson("{\"a\":[]]}", JsonParserTestPojo.class); + fail("IllegalStateException should be thrown"); + } catch (JsonbException jbe) { + throwable = jbe.getCause(); + } + + assertInstanceOf(IllegalStateException.class, throwable); + }); + } + } +} \ No newline at end of file diff --git a/src/test/java/org/eclipse/yasson/serializers/model/Cars.java b/src/test/java/org/eclipse/yasson/serializers/model/Cars.java new file mode 100644 index 000000000..9bd929bb9 --- /dev/null +++ b/src/test/java/org/eclipse/yasson/serializers/model/Cars.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2024 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.serializers.model; + +import jakarta.json.bind.annotation.JsonbProperty; + +public enum Cars { + @JsonbProperty("Ford") + FORD, + FIAT +} + diff --git a/src/test/java/org/eclipse/yasson/serializers/model/Colors.java b/src/test/java/org/eclipse/yasson/serializers/model/Colors.java new file mode 100644 index 000000000..777ebba9d --- /dev/null +++ b/src/test/java/org/eclipse/yasson/serializers/model/Colors.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2024 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.serializers.model; + +import jakarta.json.bind.annotation.JsonbProperty; +import jakarta.json.bind.annotation.JsonbTypeDeserializer; +import jakarta.json.bind.annotation.JsonbTypeSerializer; + +@JsonbTypeSerializer(ColorsSerializer.class) +@JsonbTypeDeserializer(ColorsDeserializer.class) +public enum Colors { + @JsonbProperty("Red") + RED, + @JsonbProperty("Green") + GREEN +} + diff --git a/src/test/java/org/eclipse/yasson/serializers/model/ColorsDeserializer.java b/src/test/java/org/eclipse/yasson/serializers/model/ColorsDeserializer.java new file mode 100644 index 000000000..599121003 --- /dev/null +++ b/src/test/java/org/eclipse/yasson/serializers/model/ColorsDeserializer.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2024 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.serializers.model; + +public class ColorsDeserializer extends EnumWithJsonbPropertyDeserializer { + + public ColorsDeserializer() { + super(); + } +} diff --git a/src/test/java/org/eclipse/yasson/serializers/model/ColorsSerializer.java b/src/test/java/org/eclipse/yasson/serializers/model/ColorsSerializer.java new file mode 100644 index 000000000..b2eb438c7 --- /dev/null +++ b/src/test/java/org/eclipse/yasson/serializers/model/ColorsSerializer.java @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2024 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.serializers.model; + +public class ColorsSerializer extends EnumWithJsonbPropertySerializer { + public ColorsSerializer() { + super(); + } +} diff --git a/src/test/java/org/eclipse/yasson/serializers/model/Counter.java b/src/test/java/org/eclipse/yasson/serializers/model/Counter.java new file mode 100644 index 000000000..929300640 --- /dev/null +++ b/src/test/java/org/eclipse/yasson/serializers/model/Counter.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018, 2024 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.serializers.model; + +public class Counter { + + private long counter = 0; + + public Counter() {} + + public void add() { + counter++; + } + + public long getCount() { + return counter; + } + + public void resetCount() { + counter=0; + } +} diff --git a/src/test/java/org/eclipse/yasson/serializers/model/EnumWithJsonbPropertyDeserializer.java b/src/test/java/org/eclipse/yasson/serializers/model/EnumWithJsonbPropertyDeserializer.java new file mode 100644 index 000000000..3c20656a2 --- /dev/null +++ b/src/test/java/org/eclipse/yasson/serializers/model/EnumWithJsonbPropertyDeserializer.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2024 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.serializers.model; + +import static java.util.Optional.ofNullable; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Map; + +import org.eclipse.yasson.adapters.model.EnumJsonbPropertyMaps; + +import jakarta.json.bind.serializer.DeserializationContext; +import jakarta.json.bind.serializer.JsonbDeserializer; +import jakarta.json.stream.JsonParser; + +public class EnumWithJsonbPropertyDeserializer> implements JsonbDeserializer { + + private final Map jsonToJavaMapping; + + public EnumWithJsonbPropertyDeserializer() { + super(); + + EnumJsonbPropertyMaps enumMappingMaps = new EnumJsonbPropertyMaps<>(getEnumType()); + jsonToJavaMapping = enumMappingMaps.getJsonToJavaMapping(); + } + + private Class getEnumType() { + @SuppressWarnings("unchecked") + Class cast = Class.class.cast(((ParameterizedType) getClass().getGenericSuperclass()) + .getActualTypeArguments()[0]); + return cast; + } + + @Override + public E deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { + String key = parser.getString(); + + assert key != null; + + return ofNullable(jsonToJavaMapping.get(key)) + .orElseThrow(() -> new IllegalArgumentException("Unknown enum value: '" + key + "'")); + } +} diff --git a/src/test/java/org/eclipse/yasson/serializers/model/EnumWithJsonbPropertySerializer.java b/src/test/java/org/eclipse/yasson/serializers/model/EnumWithJsonbPropertySerializer.java new file mode 100644 index 000000000..232267b7f --- /dev/null +++ b/src/test/java/org/eclipse/yasson/serializers/model/EnumWithJsonbPropertySerializer.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2024 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.serializers.model; + +import java.lang.reflect.ParameterizedType; +import java.util.EnumMap; + +import org.eclipse.yasson.adapters.model.EnumJsonbPropertyMaps; + +import jakarta.json.bind.serializer.JsonbSerializer; +import jakarta.json.bind.serializer.SerializationContext; +import jakarta.json.stream.JsonGenerator; + +public class EnumWithJsonbPropertySerializer> implements JsonbSerializer { + private final EnumMap javaToJsonMapping; + + public EnumWithJsonbPropertySerializer() { + super(); + + EnumJsonbPropertyMaps enumMappingMaps = new EnumJsonbPropertyMaps<>(getEnumType()); + javaToJsonMapping = enumMappingMaps.getJavaToJsonMapping(); + } + + private Class getEnumType() { + @SuppressWarnings("unchecked") + Class cast = Class.class.cast(((ParameterizedType) getClass().getGenericSuperclass()) + .getActualTypeArguments()[0]); + return cast; + } + + @Override public void serialize(E obj, JsonGenerator generator, SerializationContext ctx) { + generator.write(javaToJsonMapping.get(obj)); + } +} diff --git a/src/test/java/org/eclipse/yasson/serializers/model/JsonParserTestDeserializers.java b/src/test/java/org/eclipse/yasson/serializers/model/JsonParserTestDeserializers.java new file mode 100644 index 000000000..e6d2cb8bc --- /dev/null +++ b/src/test/java/org/eclipse/yasson/serializers/model/JsonParserTestDeserializers.java @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2024 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.serializers.model; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.stream.Collectors; + +import jakarta.json.JsonObject; +import jakarta.json.JsonString; +import jakarta.json.bind.JsonbException; +import jakarta.json.bind.serializer.DeserializationContext; +import jakarta.json.bind.serializer.JsonbDeserializer; +import jakarta.json.stream.JsonParser; + +import org.eclipse.yasson.internal.DeserializationContextImpl; + +import static org.eclipse.yasson.Jsonbs.defaultJsonb; + +public class JsonParserTestDeserializers { + + public static class JsonParserTestNoSuchElementExceptionDeserializer implements JsonbDeserializer { + @Override + public JsonParserTestPojo deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { + try { + parser.next(); + } catch (NoSuchElementException e) { + throw new JsonbException("Level below zero", e); + } + return new JsonParserTestPojo(); + } + } + + public static class JsonParserTestValueStreamDeserializer implements JsonbDeserializer { + @Override + public JsonParserTestPojo deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { + JsonObject value = parser.getValueStream().findFirst().orElseThrow().asJsonObject(); + return defaultJsonb.fromJson(value.toString(), JsonParserTestPojo.class); + } + } + + public static class JsonParserTestGetObjectDeserializer implements JsonbDeserializer { + + @Override + public JsonParserTestPojo deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { + parser.getObject(); + return new JsonParserTestPojo(); + } + } + + public static class JsonParserTestGetArrayDeserializer implements JsonbDeserializer { + @Override + public JsonParserTestPojo deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { + parser.getArray(); + return new JsonParserTestPojo(); + } + } + + public static class JsonParserTestEndOfObjectDeserializer implements JsonbDeserializer { + @Override + public JsonParserTestPojo deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { + //un-comment as soon as Parsson bug #112 is fixed +// parser.next(); + parser.skipObject(); + parser.getValue(); + return new JsonParserTestPojo(); + } + } + + public static class JsonParserTestEndOfArrayDeserializer implements JsonbDeserializer { + @Override + public JsonParserTestPojo deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { + parser.next(); + parser.next(); + parser.getString(); + parser.next(); + parser.skipArray(); + parser.getValue(); + return new JsonParserTestPojo(); + } + } + + public static class JsonParserTestObjectDeserializer implements JsonbDeserializer { + + private boolean integralNumber; + private String location; + private final List keyNames = new ArrayList<>(); + private final List parserEvents = new ArrayList<>(); + private final List contextEvents = new ArrayList<>(); + + @Override + public JsonParserTestPojo deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { + JsonParserTestPojo result = new JsonParserTestPojo(); + if (ctx instanceof DeserializationContextImpl) { + DeserializationContextImpl deserializationContext = (DeserializationContextImpl) ctx; + skipKey(parser, deserializationContext); + parser.skipArray(); + skipKey(parser, deserializationContext); + parser.skipObject(); + skipKey(parser, deserializationContext); + integralNumber = parser.isIntegralNumber(); + location = parser.getLocation().toString(); + result.bigDecimal = parser.getBigDecimal(); + skipKey(parser, deserializationContext); + result.integer = parser.getInt(); + skipKey(parser, deserializationContext); + result.longValue = parser.getLong(); + skipKey(parser, deserializationContext); + result.string = parser.getString(); + skipKey(parser, deserializationContext); + result.stringList = parser.getArray().stream().map(value -> ((JsonString) value).getString()).collect(Collectors.toList()); + skipKey(parser, deserializationContext); + result.stringList_getStream = parser.getArrayStream().map(value -> ((JsonString) value).getString()).collect(Collectors.toList()); + skipKey(parser, deserializationContext); + result.stringList_getValue = + parser.getValue().asJsonArray().stream().map(value -> ((JsonString) value).getString()).collect(Collectors.toList()); + skipKey(parser, deserializationContext); + result.string_getValue = ((JsonString) parser.getValue()).getString(); + skipKey(parser, deserializationContext); + result.subPojo = new JsonParserTestPojo.JsonParserTestSubPojo(parser.getObject().getString("name")); + skipKey(parser, deserializationContext); + result.subPojo_getStream = parser.getObjectStream() + .map(o -> new JsonParserTestPojo.JsonParserTestSubPojo(((JsonString) o.getValue()).getString())) + .collect(Collectors.toList()).get(0); + //following can't be tested before Parsson bug #112 is fixed. + /*.findFirst().orElse(null); + parser.skipObject();*/ + skipKey(parser, deserializationContext); + result.subPojo_getValue = new JsonParserTestPojo.JsonParserTestSubPojo(parser.getValue().asJsonObject().getString("name")); + } + return result; + } + + private void skipKey(JsonParser parser, DeserializationContextImpl deserializationContext) { + parser.hasNext(); + parserEvents.add(parser.currentEvent()); + contextEvents.add(deserializationContext.getLastValueEvent()); + parser.next(); + String key = parser.getString(); + keyNames.add(key); + parser.next(); + } + + public boolean isIntegralNumber() { + return integralNumber; + } + + public String getLocation() { + return location; + } + + public List getKeyNames() { + return Collections.unmodifiableList(keyNames); + } + + public List getParserEvents() { + return Collections.unmodifiableList(parserEvents); + } + + public List getContextEvents() { + return Collections.unmodifiableList(contextEvents); + } + } +} diff --git a/src/test/java/org/eclipse/yasson/serializers/model/JsonParserTestPojo.java b/src/test/java/org/eclipse/yasson/serializers/model/JsonParserTestPojo.java new file mode 100644 index 000000000..1119b3d5d --- /dev/null +++ b/src/test/java/org/eclipse/yasson/serializers/model/JsonParserTestPojo.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2024 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.serializers.model; + +import java.math.BigDecimal; +import java.util.List; + +public class JsonParserTestPojo { + public String string; + public Integer integer; + public Long longValue; + public BigDecimal bigDecimal; + public JsonParserTestSubPojo subPojo; + public List stringList; + public JsonParserTestSubPojo subPojo_getValue; + public String string_getValue; + public List stringList_getValue; + public List stringList_getStream; + public JsonParserTestSubPojo subPojo_getStream; + + public JsonParserTestPojo() { + } + + public JsonParserTestPojo init() { + string = "string"; + integer = 1; + longValue = 2L; + bigDecimal = BigDecimal.TEN; + subPojo = new JsonParserTestSubPojo("subPojo"); + stringList = List.of("string1", "string2"); + subPojo_getValue = new JsonParserTestSubPojo("subPojo_getValue"); + string_getValue = "string_getValue"; + stringList_getValue = List.of("string3", "string4"); + stringList_getStream = List.of("string5", "string6"); + subPojo_getStream = new JsonParserTestSubPojo("subPojo_getStream"); + return this; + } + + @Override + public boolean equals(Object obj) { + return obj != null && + getClass().isAssignableFrom(obj.getClass()) && + TwoObjectsComparer.getDifferentFieldInTwoObjects(this, obj).isEmpty(); + } + + public static class JsonParserTestSubPojo { + + public String name; + + protected JsonParserTestSubPojo() { + } + + JsonParserTestSubPojo(String name) { + this.name = name; + } + + @Override + public boolean equals(Object obj) { + return obj != null && + getClass().isAssignableFrom(obj.getClass()) && + TwoObjectsComparer.getDifferentFieldInTwoObjects(this, obj).isEmpty(); + } + } +} diff --git a/src/test/java/org/eclipse/yasson/serializers/model/NumberDeserializer.java b/src/test/java/org/eclipse/yasson/serializers/model/NumberDeserializer.java index c5bd8f2f7..7749da9a3 100644 --- a/src/test/java/org/eclipse/yasson/serializers/model/NumberDeserializer.java +++ b/src/test/java/org/eclipse/yasson/serializers/model/NumberDeserializer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024 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 @@ -18,8 +18,19 @@ import java.lang.reflect.Type; public class NumberDeserializer implements JsonbDeserializer { + + private final static Counter COUNTER = new Counter(); + + public static Counter getCounter() { + return COUNTER; + } + + { + COUNTER.add(); + } + @Override public Number deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { - return Integer.valueOf(parser.getString()) - 1; + return Integer.parseInt(parser.getString()) - 1; } } diff --git a/src/test/java/org/eclipse/yasson/serializers/model/NumberSerializer.java b/src/test/java/org/eclipse/yasson/serializers/model/NumberSerializer.java index 9ec4f1fc3..37a37ba1d 100644 --- a/src/test/java/org/eclipse/yasson/serializers/model/NumberSerializer.java +++ b/src/test/java/org/eclipse/yasson/serializers/model/NumberSerializer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024 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 @@ -17,6 +17,17 @@ import jakarta.json.stream.JsonGenerator; public class NumberSerializer implements JsonbSerializer { + + private final static Counter COUNTER = new Counter(); + + public static Counter getCounter() { + return COUNTER; + } + + { + COUNTER.add(); + } + @Override public void serialize(Number obj, JsonGenerator generator, SerializationContext ctx) { generator.write(Integer.valueOf(obj.intValue() + 1).toString()); diff --git a/src/test/java/org/eclipse/yasson/serializers/model/TwoObjectsComparer.java b/src/test/java/org/eclipse/yasson/serializers/model/TwoObjectsComparer.java new file mode 100644 index 000000000..7356083f5 --- /dev/null +++ b/src/test/java/org/eclipse/yasson/serializers/model/TwoObjectsComparer.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2024 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.serializers.model; + +import java.util.Arrays; +import java.util.Objects; +import java.util.Optional; + +public class TwoObjectsComparer { + public final String propertyName; + public final Object firstObjectValue; + public final Object secondObjectValue; + + public static Optional getDifferentFieldInTwoObjects(T thisObject, T otherObject) { + return otherObject == null ? Optional.empty() : Arrays.stream(thisObject.getClass().getFields()).map(f -> { + try { + return new TwoObjectsComparer(f.getName(), f.get(thisObject), f.get(otherObject)); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + }).filter(o -> !Objects.equals(o.firstObjectValue, o.secondObjectValue)).findFirst(); + } + + private TwoObjectsComparer(String name, Object firstObjectValue, Object secondObjectValue) { + this.propertyName = name; + this.firstObjectValue = firstObjectValue; + this.secondObjectValue = secondObjectValue; + } + + @Override + public String toString() { + return "propertyName: " + propertyName + " firstObjectValue: " + firstObjectValue + " secondObjectValue: " + secondObjectValue; + } +} diff --git a/src/test/resources/test.policy b/src/test/resources/test.policy index e247b53da..e8f5a4783 100644 --- a/src/test/resources/test.policy +++ b/src/test/resources/test.policy @@ -14,4 +14,9 @@ grant { permission "java.util.PropertyPermission" "jsonb.creator-parameters-required", "read"; permission "java.util.PropertyPermission" "yasson.time-in-millis-as-a-string", "read"; permission "java.util.PropertyPermission" "jakarta.json.provider", "read"; + permission "java.util.PropertyPermission" "org.eclipse.parsson.maxBigIntegerScale", "read"; + permission "java.util.PropertyPermission" "org.eclipse.parsson.maxBigDecimalLength", "read"; + permission "java.util.PropertyPermission" "org.eclipse.parsson.maxDepth", "read"; + permission "java.util.PropertyPermission" "org.eclipse.parsson.rejectDuplicateKeys", "read"; + permission "java.util.PropertyPermission" "jakarta.json.stream.JsonGenerator.prettyPrinting", "read"; }; \ No newline at end of file