Fix #627: Reuse registered serializer and deserializer instances ComponentMatcher.introspectSerializerBinding() and introspectDeserializerBinding() are meant to return the binding of a serializer or deserializer instance registered in the JsonbConfig when it is of the requested class, but they compared the class of the binding (SerializerBinding, DeserializerBinding) instead of the class of the component it wraps, which never matched. So a property annotated with @JsonbTypeSerializer or @JsonbTypeDeserializer always got a new default instance, ignoring the configured one. Compare the class of the wrapped component, as introspectAdapterBinding() already does. Signed-off-by: Sharang Gupta <sharang@sharanggupta.dev>
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/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); + } + } +}