Merge pull request #792 from sharanggupta/fix-456-setter-error-message

Fix #456: Do not call toString() of an object whose setter fails
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 c7870b7..cb49652 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,6 @@
 /*
  * Copyright (c) 2021, 2023 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
@@ -112,11 +113,36 @@
         //If the class instance and class of the field are the same and there has been generics specified for this field,
         //we need to use those instead of raw type.
         Class<?> rawType = ReflectionUtils.getRawType(type);
+        Customization customization = runtimeTypeCustomization(rawType, propertyCustomization);
         Class<?> rawLast = ReflectionUtils.getRawType(chain.getLast());
         if (rawLast.equals(rawType)) {
-            return serializerChainInternal(chain, chain.getLast(), propertyCustomization, rootValue, isKey, true);
+            return serializerChainInternal(chain, chain.getLast(), customization, rootValue, isKey, true);
         }
-        return serializerChainInternal(chain, type, propertyCustomization, rootValue, isKey, true);
+        return serializerChainInternal(chain, type, customization, rootValue, isKey, true);
+    }
+
+    /**
+     * Return the customization to create the serializer of a type only known at runtime with.
+     * The given customization is the one of the declared type, for example {@link Object} for the elements of a raw
+     * collection, so an adapter bound to the class of the runtime type with {@code @JsonbTypeAdapter} is taken from
+     * the customization of that class when the given customization binds neither a serializer nor an adapter.
+     * A serializer bound to the class with {@code @JsonbTypeSerializer} is already found by
+     * {@link ComponentMatcher#getSerializerBinding}.
+     *
+     * @param runtimeType   runtime type of the value
+     * @param customization customization of the declared type
+     * @return customization to use
+     */
+    private Customization runtimeTypeCustomization(Class<?> runtimeType, Customization customization) {
+        ComponentBoundCustomization declared = (ComponentBoundCustomization) customization;
+        if (declared != null
+                && (declared.getSerializerBinding() != null || declared.getSerializeAdapterBinding() != null)) {
+            return customization;
+        }
+        ClassCustomization classCustomization = jsonbContext.getMappingContext()
+                .getOrCreateClassModel(runtimeType)
+                .getClassCustomization();
+        return classCustomization.getSerializeAdapterBinding() != null ? classCustomization : customization;
     }
 
     private ModelSerializer serializerChain(LinkedList<Type> chain,
diff --git a/src/test/java/org/eclipse/yasson/adapters/JsonbTypeAdapterTest.java b/src/test/java/org/eclipse/yasson/adapters/JsonbTypeAdapterTest.java
index 3ee82da..4bfb142 100644
--- a/src/test/java/org/eclipse/yasson/adapters/JsonbTypeAdapterTest.java
+++ b/src/test/java/org/eclipse/yasson/adapters/JsonbTypeAdapterTest.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
@@ -19,6 +20,10 @@
 import org.eclipse.yasson.TestTypeToken;
 import org.eclipse.yasson.adapters.model.*;
 
+import java.util.List;
+
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbBuilder;
 import jakarta.json.bind.JsonbException;
 import jakarta.json.bind.adapter.JsonbAdapter;
 import jakarta.json.bind.annotation.JsonbTypeAdapter;
@@ -127,6 +132,33 @@
         public boolean bool;
     }
 
+    @JsonbTypeAdapter(AnnotatedTypeAdapter.class)
+    public static class AnnotatedType {
+        public String field = "value";
+    }
+
+    public static class AnnotatedTypeAdapter implements JsonbAdapter<AnnotatedType, String> {
+
+        @Override
+        public String adaptToJson(AnnotatedType obj) {
+            return "adapted";
+        }
+
+        @Override
+        public AnnotatedType adaptFromJson(String obj) {
+            return new AnnotatedType();
+        }
+    }
+
+    @Test
+    public void testClassAdapterOfCollectionElement() throws Exception {
+        // Must use a new Jsonb instance to make sure adapter cache is empty
+        try (Jsonb jsonb = JsonbBuilder.create()) {
+            assertEquals("[\"adapted\"]", jsonb.toJson(List.of(new AnnotatedType())));
+            assertEquals("\"adapted\"", jsonb.toJson(new AnnotatedType()));
+        }
+    }
+
     @Test
     public void testPrimitiveDeserialzier() {
         String successfulJson = "{\"bool\":true}";
diff --git a/src/test/java/org/eclipse/yasson/serializers/SerializersTest.java b/src/test/java/org/eclipse/yasson/serializers/SerializersTest.java
index 8214b88..26a829a 100644
--- a/src/test/java/org/eclipse/yasson/serializers/SerializersTest.java
+++ b/src/test/java/org/eclipse/yasson/serializers/SerializersTest.java
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2016, 2025 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
@@ -839,6 +840,27 @@
 
     }
 
+    @JsonbTypeSerializer(SerializedTypeSerializer.class)
+    public static class SerializedType {
+        public String field = "value";
+    }
+
+    public static class SerializedTypeSerializer implements JsonbSerializer<SerializedType> {
+        @Override
+        public void serialize(SerializedType obj, JsonGenerator generator, SerializationContext ctx) {
+            generator.write("serialized");
+        }
+    }
+
+    @Test
+    public void testClassSerializerOfCollectionElement() throws Exception {
+        // Must use a new Jsonb instance to make sure serializer cache is empty
+        try (Jsonb jsonb = JsonbBuilder.create()) {
+            assertEquals("[\"serialized\"]", jsonb.toJson(List.of(new SerializedType())));
+            assertEquals("\"serialized\"", jsonb.toJson(new SerializedType()));
+        }
+    }
+
     /**
      * Test that annotation-based serializers work when property is declared as Object
      * but the runtime type has @JsonbTypeSerializer annotation.