Merge pull request #788 from sharanggupta/fix-609-array-into-string Fix #609: Reject a JSON array or object where a value is expected
diff --git a/pom.xml b/pom.xml index dde3171..5f0f011 100644 --- a/pom.xml +++ b/pom.xml
@@ -61,7 +61,7 @@ <!--Plugins--> <build-helper-maven-plugin.version>3.6.0</build-helper-maven-plugin.version> <buildnumber-maven-plugin.version>3.3.1</buildnumber-maven-plugin.version> - <spotbugs-maven-plugin.version>4.10.4.1</spotbugs-maven-plugin.version> + <spotbugs-maven-plugin.version>4.10.5.0</spotbugs-maven-plugin.version> <glassfish-copyright-maven-plugin.version>2.4</glassfish-copyright-maven-plugin.version> <maven-bundle-plugin.version>5.1.9</maven-bundle-plugin.version> <maven-checkstyle-plugin.version>3.3.1</maven-checkstyle-plugin.version>
diff --git a/src/main/java/org/eclipse/yasson/internal/AnnotationIntrospector.java b/src/main/java/org/eclipse/yasson/internal/AnnotationIntrospector.java index 2228c51..8edc2af 100644 --- a/src/main/java/org/eclipse/yasson/internal/AnnotationIntrospector.java +++ b/src/main/java/org/eclipse/yasson/internal/AnnotationIntrospector.java
@@ -1,5 +1,6 @@ /* * Copyright (c) 2016, 2026 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026 Contributors to the Eclipse Foundation * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -594,9 +595,16 @@ */ public JsonbDateFormatter getConstructorDateFormatter(JsonbAnnotatedElement<Parameter> param) { return param.getAnnotation(JsonbDateFormat.class) - .map(annotation -> new JsonbDateFormatter(DateTimeFormatter.ofPattern(annotation.value(), - Locale.forLanguageTag(annotation.locale())), - annotation.value(), annotation.locale())) + .map(annotation -> { + String format = annotation.value(); + String locale = annotation.locale(); + if (JsonbDateFormat.TIME_IN_MILLIS.equals(format) || JsonbDateFormat.DEFAULT_FORMAT.equals(format)) { + //not patterns, handled by the date converters as for properties + return new JsonbDateFormatter(format, locale); + } + return new JsonbDateFormatter(DateTimeFormatter.ofPattern(format, Locale.forLanguageTag(locale)), + format, locale); + }) .orElse(null); }
diff --git a/src/main/java/org/eclipse/yasson/internal/ComponentMatcher.java b/src/main/java/org/eclipse/yasson/internal/ComponentMatcher.java index 2763ea9..b27b63f 100644 --- a/src/main/java/org/eclipse/yasson/internal/ComponentMatcher.java +++ b/src/main/java/org/eclipse/yasson/internal/ComponentMatcher.java
@@ -1,5 +1,6 @@ /* * Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026 Contributors to the Eclipse Foundation * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -418,7 +419,7 @@ .findParameterizedType(deserializerClass, JsonbDeserializer.class); Type deserializerBindingType = resolveTypeArg(deserializerRuntimeType.getActualTypeArguments()[0], deserializerClass); final ComponentBindings componentBindings = getBindingInfo(deserializerBindingType); - if (componentBindings.getDeserializer() != null && componentBindings.getDeserializer().getClass() + if (componentBindings.getDeserializer() != null && componentBindings.getDeserializer().getJsonbDeserializer().getClass() .equals(deserializerClass)) { return componentBindings.getDeserializer(); } else { @@ -442,7 +443,8 @@ .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)) { + if (componentBindings.getSerializer() != null + && componentBindings.getSerializer().getJsonbSerializer().getClass().equals(serializerClass)) { return componentBindings.getSerializer(); } else { JsonbSerializer serializer = instance != null ? instance : jsonbContext.getComponentInstanceCreator()
diff --git a/src/test/java/org/eclipse/yasson/records/RecordTest.java b/src/test/java/org/eclipse/yasson/records/RecordTest.java index edaee35..bf3ad00 100644 --- a/src/test/java/org/eclipse/yasson/records/RecordTest.java +++ b/src/test/java/org/eclipse/yasson/records/RecordTest.java
@@ -24,6 +24,8 @@ import org.eclipse.yasson.internal.properties.Messages; import org.junit.jupiter.api.Test; +import java.time.Instant; +import java.time.LocalDate; import java.util.List; import java.util.Set; @@ -50,6 +52,18 @@ } @Test + public void testRecordWithDateFormats() { + String json = "{\"iso\":\"2024-10-11T13:28:22Z\",\"localeOnly\":\"2024-10-11\",\"millis\":1728574424000}"; + + RecordWithDateFormats deserialized = Jsonbs.defaultJsonb.fromJson(json, RecordWithDateFormats.class); + + assertEquals(Instant.ofEpochMilli(1728574424000L), deserialized.millis()); + assertEquals(LocalDate.of(2024, 10, 11), deserialized.localeOnly()); + assertEquals(Instant.parse("2024-10-11T13:28:22Z"), deserialized.iso()); + assertEquals(json, Jsonbs.defaultJsonb.toJson(deserialized)); + } + + @Test public void testRecordProcessingWithoutJsonbProperties() { CarWithoutAnnotations car = new CarWithoutAnnotations("skoda", "green"); String expected = "{\"color\":\"green\",\"type\":\"skoda\"}";
diff --git a/src/test/java/org/eclipse/yasson/records/RecordWithDateFormats.java b/src/test/java/org/eclipse/yasson/records/RecordWithDateFormats.java new file mode 100644 index 0000000..a247f13 --- /dev/null +++ b/src/test/java/org/eclipse/yasson/records/RecordWithDateFormats.java
@@ -0,0 +1,23 @@ +/* + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +package org.eclipse.yasson.records; + +import java.time.Instant; +import java.time.LocalDate; + +import jakarta.json.bind.annotation.JsonbDateFormat; + +public record RecordWithDateFormats(@JsonbDateFormat(JsonbDateFormat.TIME_IN_MILLIS) Instant millis, + @JsonbDateFormat(locale = "de") LocalDate localeOnly, + Instant iso) { +}
diff --git a/src/test/java/org/eclipse/yasson/serializers/RegisteredComponentReuseTest.java b/src/test/java/org/eclipse/yasson/serializers/RegisteredComponentReuseTest.java new file mode 100644 index 0000000..ec5b661 --- /dev/null +++ b/src/test/java/org/eclipse/yasson/serializers/RegisteredComponentReuseTest.java
@@ -0,0 +1,108 @@ +/* + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * 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; + +import java.lang.reflect.Type; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbConfig; +import jakarta.json.bind.annotation.JsonbTypeDeserializer; +import jakarta.json.bind.annotation.JsonbTypeSerializer; +import jakarta.json.bind.serializer.DeserializationContext; +import jakarta.json.bind.serializer.JsonbDeserializer; +import jakarta.json.bind.serializer.JsonbSerializer; +import jakarta.json.bind.serializer.SerializationContext; +import jakarta.json.stream.JsonGenerator; +import jakarta.json.stream.JsonParser; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * A serializer or deserializer instance registered in the {@link JsonbConfig} is used for the + * properties annotated with its class, instead of a new instance + * (<a href="https://github.com/eclipse-ee4j/yasson/issues/627">#627</a>). + */ +public class RegisteredComponentReuseTest { + + public static class PrefixSerializer implements JsonbSerializer<String> { + + private final String prefix; + + public PrefixSerializer() { + this("new:"); + } + + public PrefixSerializer(String prefix) { + this.prefix = prefix; + } + + @Override + public void serialize(String obj, JsonGenerator generator, SerializationContext ctx) { + generator.write(prefix + obj); + } + } + + public static class PrefixDeserializer implements JsonbDeserializer<String> { + + private final String prefix; + + public PrefixDeserializer() { + this("new:"); + } + + public PrefixDeserializer(String prefix) { + this.prefix = prefix; + } + + @Override + public String deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { + return prefix + parser.getString(); + } + } + + public static class Annotated { + + @JsonbTypeSerializer(PrefixSerializer.class) + @JsonbTypeDeserializer(PrefixDeserializer.class) + public String value; + } + + @Test + public void registeredSerializerIsUsedForAnnotatedProperty() throws Exception { + Annotated annotated = new Annotated(); + annotated.value = "x"; + try (Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withSerializers(new PrefixSerializer("registered:")))) { + assertEquals("{\"value\":\"registered:x\"}", jsonb.toJson(annotated)); + } + } + + @Test + public void registeredDeserializerIsUsedForAnnotatedProperty() throws Exception { + try (Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withDeserializers(new PrefixDeserializer("registered:")))) { + assertEquals("registered:x", jsonb.fromJson("{\"value\":\"x\"}", Annotated.class).value); + } + } + + @Test + public void annotatedPropertyWithoutRegisteredInstanceUsesNewInstance() throws Exception { + Annotated annotated = new Annotated(); + annotated.value = "x"; + try (Jsonb jsonb = JsonbBuilder.create()) { + assertEquals("{\"value\":\"new:x\"}", jsonb.toJson(annotated)); + assertEquals("new:x", jsonb.fromJson("{\"value\":\"x\"}", Annotated.class).value); + } + } +}