Merge pull request #686 from jamezp/issue685

[685] Ensure the value type of a container being deserialized uses th…
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 1f1bdab..6a6daca 100644
--- a/src/main/java/org/eclipse/yasson/internal/deserializer/DeserializationModelCreator.java
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/DeserializationModelCreator.java
@@ -180,15 +180,15 @@
             return typeDeserializer;
         }
         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);
+            return createMapDeserializer(cachedItem, rawType, chain);
         } else if (rawType.isArray()) {
-            return createArrayDeserializer(cachedItem, rawType, chain, propertyCustomization);
+            return createArrayDeserializer(cachedItem, rawType, chain);
         } else if (type instanceof GenericArrayType) {
-            return createGenericArray(cachedItem, rawType, chain, propertyCustomization);
+            return createGenericArray(cachedItem, rawType, chain);
         } else if (Optional.class.isAssignableFrom(rawType)) {
-            return createOptionalDeserializer(chain, type, propertyCustomization, cachedItem);
+            return createOptionalDeserializer(chain, type, cachedItem);
         } else {
             return createObjectDeserializer(chain, type, propertyCustomization, classModel, rawType, cachedItem);
         }
@@ -262,8 +262,7 @@
 
     private ModelDeserializer<JsonParser> createCollectionDeserializer(CachedItem cachedItem,
                                                                        Class<?> rawType,
-                                                                       LinkedList<Type> chain,
-                                                                       Customization propertyCustomization) {
+                                                                       LinkedList<Type> chain) {
         Type type = cachedItem.type;
         Type colType = type instanceof ParameterizedType
                 ? ((ParameterizedType) type).getActualTypeArguments()[0]
@@ -284,8 +283,7 @@
 
     private ModelDeserializer<JsonParser> createMapDeserializer(CachedItem cachedItem,
                                                                 Class<?> rawType,
-                                                                LinkedList<Type> chain,
-                                                                Customization propertyCustomization) {
+                                                                LinkedList<Type> chain) {
         Type type = cachedItem.type;
         Type keyType = type instanceof ParameterizedType
                 ? ((ParameterizedType) type).getActualTypeArguments()[0]
@@ -298,9 +296,10 @@
                                                                    ClassCustomization.empty(),
                                                                    JustReturn.instance(),
                                                                    MAP_KEY_EVENTS);
+        ClassModel valueClassModel = jsonbContext.getMappingContext().getOrCreateClassModel(ReflectionUtils.resolveRawType(chain, valueType));
         ModelDeserializer<JsonParser> valueProcessor = typeProcessor(chain,
                                                                      valueType,
-                                                                     propertyCustomization,
+                                                                     valueClassModel.getClassCustomization(),
                                                                      JustReturn.instance());
 
         MapDeserializer mapDeserializer = new MapDeserializer(keyProcessor, valueProcessor);
@@ -315,14 +314,15 @@
 
     private ModelDeserializer<JsonParser> createArrayDeserializer(CachedItem cachedItem,
                                                                   Class<?> rawType,
-                                                                  LinkedList<Type> chain,
-                                                                  Customization propertyCustomization) {
+                                                                  LinkedList<Type> chain) {
         JsonbConfigProperties configProperties = jsonbContext.getConfigProperties();
         if (rawType.equals(byte[].class) && !configProperties.getBinaryDataStrategy().equals(BinaryDataStrategy.BYTE)) {
             String strategy = configProperties.getBinaryDataStrategy();
+            // Special case for byte[] with base64 encoding - use String's class customization
+            ClassModel stringModel = jsonbContext.getMappingContext().getOrCreateClassModel(String.class);
             ModelDeserializer<JsonParser> typeProcessor = typeProcessor(chain,
                                                                         String.class,
-                                                                        propertyCustomization,
+                                                                        stringModel.getClassCustomization(),
                                                                         JustReturn.instance());
             ModelDeserializer<JsonParser> base64Deserializer = ArrayInstanceCreator.createBase64Deserializer(strategy,
                                                                                                              typeProcessor);
@@ -331,22 +331,23 @@
             return nullChecker;
         }
         Class<?> arrayType = rawType.getComponentType();
+        ClassModel classModel = jsonbContext.getMappingContext().getOrCreateClassModel(arrayType);
         ModelDeserializer<JsonParser> typeProcessor = typeProcessor(chain,
                                                                     arrayType,
-                                                                    propertyCustomization,
+                                                                    classModel.getClassCustomization(),
                                                                     JustReturn.instance());
         return createArrayCommonDeserializer(cachedItem, rawType, arrayType, typeProcessor);
     }
 
     private ModelDeserializer<JsonParser> createGenericArray(CachedItem cachedItem,
                                                              Class<?> rawType,
-                                                             LinkedList<Type> chain,
-                                                             Customization propertyCustomization) {
+                                                             LinkedList<Type> chain) {
         GenericArrayType type = (GenericArrayType) cachedItem.type;
         Class<?> component = ReflectionUtils.getRawType(type.getGenericComponentType());
+        ClassModel classModel = jsonbContext.getMappingContext().getOrCreateClassModel(component);
         ModelDeserializer<JsonParser> typeProcessor = typeProcessor(chain,
                                                                     type.getGenericComponentType(),
-                                                                    propertyCustomization,
+                                                                    classModel.getClassCustomization(),
                                                                     JustReturn.instance());
         return createArrayCommonDeserializer(cachedItem, rawType, component, typeProcessor);
     }
@@ -365,12 +366,13 @@
 
     private OptionalDeserializer createOptionalDeserializer(LinkedList<Type> chain,
                                                             Type type,
-                                                            Customization propertyCustomization,
                                                             CachedItem cachedItem) {
         Type colType = type instanceof ParameterizedType
                 ? ((ParameterizedType) type).getActualTypeArguments()[0]
                 : Object.class;
-        ModelDeserializer<JsonParser> typeProcessor = typeProcessor(chain, colType, propertyCustomization, JustReturn.instance());
+        colType = ReflectionUtils.resolveType(chain, colType);
+        ClassModel classModel = jsonbContext.getMappingContext().getOrCreateClassModel(ReflectionUtils.getRawType(colType));
+        ModelDeserializer<JsonParser> typeProcessor = typeProcessor(chain, colType, classModel.getClassCustomization(), JustReturn.instance());
         OptionalDeserializer optionalDeserializer = new OptionalDeserializer(typeProcessor, JustReturn.instance());
         models.put(cachedItem, optionalDeserializer);
         return optionalDeserializer;
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 0379c55..9aa29a7 100644
--- a/src/main/java/org/eclipse/yasson/internal/serializer/SerializationModelCreator.java
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/SerializationModelCreator.java
@@ -302,7 +302,9 @@
         Type resolvedKey = ReflectionUtils.resolveType(chain, keyType);
         Class<?> rawClass = ReflectionUtils.getRawType(resolvedKey);
         ModelSerializer keySerializer = memberSerializer(chain, keyType, ClassCustomization.empty(), true);
-        ModelSerializer valueSerializer = memberSerializer(chain, valueType, propertyCustomization, false);
+        Type resolvedValue = ReflectionUtils.resolveType(chain, valueType);
+        ClassModel valueClassModel = jsonbContext.getMappingContext().getOrCreateClassModel(ReflectionUtils.getRawType(resolvedValue));
+        ModelSerializer valueSerializer = memberSerializer(chain, valueType, valueClassModel.getClassCustomization(), false);
         MapSerializer mapSerializer = MapSerializer.create(rawClass, keySerializer, valueSerializer, jsonbContext);
         KeyWriter keyWriter = new KeyWriter(mapSerializer);
         NullVisibilitySwitcher nullVisibilitySwitcher = new NullVisibilitySwitcher(true, keyWriter);
@@ -313,7 +315,8 @@
                                                   Class<?> raw,
                                                   Customization propertyCustomization) {
         Class<?> arrayComponent = raw.getComponentType();
-        ModelSerializer modelSerializer = memberSerializer(chain, arrayComponent, propertyCustomization, false);
+        ClassModel classModel = jsonbContext.getMappingContext().getOrCreateClassModel(arrayComponent);
+        ModelSerializer modelSerializer = memberSerializer(chain, arrayComponent, classModel.getClassCustomization(), false);
         ModelSerializer arraySerializer = ArraySerializer.create(raw, jsonbContext, modelSerializer);
         KeyWriter keyWriter = new KeyWriter(arraySerializer);
         NullVisibilitySwitcher nullVisibilitySwitcher = new NullVisibilitySwitcher(true, keyWriter);
@@ -325,7 +328,8 @@
                                                          Customization propertyCustomization) {
         Class<?> raw = ReflectionUtils.getRawType(type);
         Class<?> component = ReflectionUtils.getRawType(((GenericArrayType) type).getGenericComponentType());
-        ModelSerializer modelSerializer = memberSerializer(chain, component, propertyCustomization, false);
+        ClassModel classModel = jsonbContext.getMappingContext().getOrCreateClassModel(component);
+        ModelSerializer modelSerializer = memberSerializer(chain, component, classModel.getClassCustomization(), false);
         ModelSerializer arraySerializer = ArraySerializer.create(raw, jsonbContext, modelSerializer);
         KeyWriter keyWriter = new KeyWriter(arraySerializer);
         NullVisibilitySwitcher nullVisibilitySwitcher = new NullVisibilitySwitcher(true, keyWriter);
@@ -339,7 +343,9 @@
         Type optType = type instanceof ParameterizedType
                 ? ((ParameterizedType) type).getActualTypeArguments()[0]
                 : Object.class;
-        ModelSerializer modelSerializer = memberSerializer(chain, optType, propertyCustomization, isKey);
+        Type resolvedOptType = ReflectionUtils.resolveType(chain, optType);
+        ClassModel classModel = jsonbContext.getMappingContext().getOrCreateClassModel(ReflectionUtils.getRawType(resolvedOptType));
+        ModelSerializer modelSerializer = memberSerializer(chain, optType, classModel.getClassCustomization(), isKey);
         return new OptionalSerializer(modelSerializer);
     }
 
diff --git a/src/test/java/org/eclipse/yasson/serializers/TypeDeserializerOnContainersTest.java b/src/test/java/org/eclipse/yasson/serializers/TypeDeserializerOnContainersTest.java
new file mode 100644
index 0000000..7cd1e3f
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/TypeDeserializerOnContainersTest.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2025 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
+ * 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 java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+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.config.BinaryDataStrategy;
+import jakarta.json.bind.serializer.DeserializationContext;
+import jakarta.json.bind.serializer.JsonbDeserializer;
+import jakarta.json.stream.JsonParser;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests that {@link jakarta.json.bind.annotation.JsonbTypeDeserializer @JsonbTypeDeserializer} annotated types are
+ * properly detected and used when those types are used as elements/values in containers (Maps, Collections,
+ * Arrays, Optionals).
+ *
+ * @author <a href="mailto:jperkins@ibm.com">James R. Perkins</a>
+ */
+public class TypeDeserializerOnContainersTest {
+
+    // Test interface with type-level deserializer annotation
+    @JsonbTypeDeserializer(TestInterfaceDeserializer.class)
+    public interface TestInterface {
+        String getValue();
+    }
+
+    // Implementation of the test interface
+    public static class TestImpl implements TestInterface {
+        private final String value;
+
+        public TestImpl(final String value) {
+            this.value = value;
+        }
+
+        @Override
+        public String getValue() {
+            return value;
+        }
+    }
+
+    // Custom deserializer for TestInterface
+    public static class TestInterfaceDeserializer implements JsonbDeserializer<TestInterface> {
+        @Override
+        public TestInterface deserialize(final JsonParser parser, final DeserializationContext ctx, final Type rtType) {
+            // Parse the JSON object to get the value field
+            Assertions.assertTrue(parser.hasNext(), "Expected the key name");
+            parser.next();
+            Assertions.assertTrue(parser.hasNext(), "Expected the value");
+            parser.next();
+            final String value = parser.getString();
+            Assertions.assertTrue(parser.hasNext(), "Expected the end of an object");
+            parser.next();
+            return new TestImpl("DESERIALIZED:" + value);
+        }
+    }
+
+    // Container classes for testing
+    public static class MapContainer {
+        public Map<String, TestInterface> map;
+        public Map<?, ?> questionKeyMap;
+        public Map<String, ?> questionValueMap;
+    }
+
+    public static class ListContainer {
+        public List<TestInterface> list;
+        public List<?> questionList;
+    }
+
+    public static class ArrayContainer {
+        public TestInterface[] array;
+    }
+
+    @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
+    public static class OptionalContainer {
+        public Optional<TestInterface> optional;
+        public Optional<?> questionOptional;
+    }
+
+    public static class ByteArrayContainer {
+        public byte[] data;
+    }
+
+    private Jsonb jsonb;
+
+    @BeforeEach
+    public void createJsonb() {
+        // Create a new Jsonb for each test to avoid type caching
+        jsonb = JsonbBuilder.create();
+    }
+
+    @AfterEach
+    public void closeJsonb() throws Exception {
+        if (jsonb != null) {
+            jsonb.close();
+        }
+    }
+
+    @Test
+    public void testTypeDeserializerOnMapValues() {
+        final String json = "{\"map\":{\"key1\":{\"value\":\"value1\"},\"key2\":{\"value\":\"value2\"}}, \"questionKeyMap\":{\"qKey1\":\"value1\",\"qKey2\":\"value2\"},\"questionValueMap\":{\"key1\":\"qValue1\",\"key2\":\"qValue2\"}}";
+
+        final MapContainer result = jsonb.fromJson(json, MapContainer.class);
+
+        Assertions.assertNotNull(result.map);
+        Assertions.assertEquals(2, result.map.size(), () -> String.format("Expected two entries got %s", result.map));
+        Assertions.assertEquals("DESERIALIZED:value1", result.map.get("key1").getValue());
+        Assertions.assertEquals("DESERIALIZED:value2", result.map.get("key2").getValue());
+
+        Assertions.assertNotNull(result.questionKeyMap);
+        Assertions.assertEquals(2, result.questionKeyMap.size(), () -> String.format("Expected two entries got %s", result.questionKeyMap));
+        Assertions.assertEquals("value1", result.questionKeyMap.get("qKey1"));
+        Assertions.assertEquals("value2", result.questionKeyMap.get("qKey2"));
+
+        Assertions.assertNotNull(result.questionValueMap);
+        Assertions.assertEquals(2, result.questionValueMap.size(), () -> String.format("Expected two entries got %s", result.questionValueMap));
+        Assertions.assertEquals("qValue1", result.questionValueMap.get("key1"));
+        Assertions.assertEquals("qValue2", result.questionValueMap.get("key2"));
+    }
+
+    @Test
+    public void testTypeDeserializerOnListElements() {
+        final String json = "{\"list\":[{\"value\":\"value1\"},{\"value\":\"value2\"}], \"questionList\": [\"value1\", \"value2\"]}";
+
+        final ListContainer result = jsonb.fromJson(json, ListContainer.class);
+
+        Assertions.assertNotNull(result.list);
+        Assertions.assertEquals(2, result.list.size(), () -> String.format("Expected two entries got %s", result.list));
+        Assertions.assertEquals("DESERIALIZED:value1", result.list.get(0).getValue());
+        Assertions.assertEquals("DESERIALIZED:value2", result.list.get(1).getValue());
+
+        Assertions.assertNotNull(result.questionList);
+        Assertions.assertEquals(2, result.questionList.size(), () -> String.format("Expected two entries got %s", result.questionList));
+        Assertions.assertEquals("value1", result.questionList.get(0));
+        Assertions.assertEquals("value2", result.questionList.get(1));
+    }
+
+    @Test
+    public void testTypeDeserializerOnArrayElements() {
+        final String json = "{\"array\":[{\"value\":\"value1\"},{\"value\":\"value2\"}]}";
+
+        final ArrayContainer result = jsonb.fromJson(json, ArrayContainer.class);
+
+        Assertions.assertNotNull(result.array);
+        Assertions.assertEquals(2, result.array.length, () -> String.format("Expected two entries got %s", Arrays.toString(result.array)));
+        Assertions.assertEquals("DESERIALIZED:value1", result.array[0].getValue());
+        Assertions.assertEquals("DESERIALIZED:value2", result.array[1].getValue());
+    }
+
+    @Test
+    public void testTypeDeserializerOnOptionalValue() {
+        final String json = "{\"optional\":{\"value\":\"value1\"},\"questionOptional\":\"value2\"}";
+
+        final OptionalContainer result = jsonb.fromJson(json, OptionalContainer.class);
+
+        Assertions.assertNotNull(result.optional);
+        Assertions.assertTrue(result.optional.isPresent(), "Expected value to be present, but the optional was empty.");
+        Assertions.assertEquals("DESERIALIZED:value1", result.optional.get().getValue());
+
+        Assertions.assertNotNull(result.questionOptional);
+        Assertions.assertTrue(result.questionOptional.isPresent(), "Expected value to be present, but the optional was empty.");
+        Assertions.assertEquals("value2", result.questionOptional.get());
+    }
+
+    @Test
+    public void testTypeDeserializerOnByteArray() {
+        final String json = "{\"data\":[1,2,3,4,5]}";
+
+        final ByteArrayContainer result = jsonb.fromJson(json, ByteArrayContainer.class);
+
+        Assertions.assertNotNull(result.data);
+        Assertions.assertEquals(5, result.data.length);
+        Assertions.assertArrayEquals(new byte[]{1, 2, 3, 4, 5}, result.data);
+    }
+
+    @Test
+    public void testTypeDeserializerOnByteArrayWithBase64() throws Exception {
+        try (Jsonb base64Jsonb = JsonbBuilder.create(new JsonbConfig()
+                .withBinaryDataStrategy(BinaryDataStrategy.BASE_64))) {
+
+            // "SGVsbG8=" is "Hello" in base64
+            final String json = "{\"data\":\"SGVsbG8=\"}";
+
+            final ByteArrayContainer result = base64Jsonb.fromJson(json, ByteArrayContainer.class);
+
+            Assertions.assertNotNull(result.data);
+            Assertions.assertArrayEquals("Hello".getBytes(), result.data);
+        }
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/TypeSerializerOnContainersTest.java b/src/test/java/org/eclipse/yasson/serializers/TypeSerializerOnContainersTest.java
new file mode 100644
index 0000000..89f3ff2
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/TypeSerializerOnContainersTest.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2025 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
+ * 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.io.StringReader;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Supplier;
+
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonReader;
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbBuilder;
+import jakarta.json.bind.JsonbException;
+import jakarta.json.bind.annotation.JsonbTypeSerializer;
+import jakarta.json.bind.serializer.JsonbSerializer;
+import jakarta.json.bind.serializer.SerializationContext;
+import jakarta.json.stream.JsonGenerator;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests that {@link jakarta.json.bind.annotation.JsonbTypeSerializer @JsonbTypeSerializer} annotated types are
+ * properly detected and used when those types are used as elements/values in containers (Maps, Collections,
+ * Arrays, Optionals).
+ *
+ * @author <a href="mailto:jperkins@ibm.com">James R. Perkins</a>
+ */
+public class TypeSerializerOnContainersTest {
+
+    // Test interface with type-level serializer annotation
+    @JsonbTypeSerializer(TestInterfaceSerializer.class)
+    public interface TestInterface {
+        String getValue();
+    }
+
+    // Implementation of the test interface
+    public static class TestImpl implements TestInterface {
+        private final String value;
+
+        public TestImpl(final String value) {
+            this.value = value;
+        }
+
+        @Override
+        public String getValue() {
+            return value;
+        }
+    }
+
+    // Custom serializer for TestInterface
+    public static class TestInterfaceSerializer implements JsonbSerializer<TestInterface> {
+        @Override
+        public void serialize(final TestInterface obj, final JsonGenerator generator, final SerializationContext ctx) {
+            generator.write("SERIALIZED:" + obj.getValue());
+        }
+    }
+
+    // Container classes for testing
+    public static class MapContainer {
+        public final Map<String, TestInterface> map;
+        public final Map<?, ?> questionKeyMap;
+        public final Map<String, ?> questionValueMap;
+
+        public MapContainer(final Map<String, TestInterface> map, final Map<?, ?> questionKeyMap, final Map<String, ?> questionValueMap) {
+            this.map = map;
+            this.questionKeyMap = questionKeyMap;
+            this.questionValueMap = questionValueMap;
+        }
+    }
+
+    public static class ListContainer {
+        public final List<TestInterface> list;
+        public final List<?> questionList;
+
+        public ListContainer(final List<TestInterface> list, final List<?> questionList) {
+            this.list = list;
+            this.questionList = questionList;
+        }
+    }
+
+    public static class ArrayContainer {
+        public final TestInterface[] array;
+
+        public ArrayContainer(TestInterface[] array) {
+            this.array = array;
+        }
+    }
+
+    @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
+    public static class OptionalContainer {
+        public final Optional<TestInterface> optional;
+        public final Optional<?> questionOptional;
+
+        public OptionalContainer(final Optional<TestInterface> optional, final Optional<?> questionOptional) {
+            this.optional = optional;
+            this.questionOptional = questionOptional;
+        }
+    }
+
+    private Jsonb jsonb;
+
+    @BeforeEach
+    public void createJsonb() {
+        // Create a new Jsonb for each test to avoid type caching
+        jsonb = JsonbBuilder.create();
+    }
+
+    @AfterEach
+    public void closeJsonb() throws Exception {
+        if (jsonb != null) {
+            jsonb.close();
+        }
+    }
+
+    @Test
+    public void testTypeSerializerOnMapValues() {
+        final MapContainer container = new MapContainer(Map.of(
+                "key1", new TestImpl("value1"),
+                "key2", new TestImpl("value2")
+        ), Map.of("qKey1", "value1", "qKey2", "value2"),
+                Map.of("key1", "qValue1", "key2", "qValue2")
+        );
+
+        final JsonObject json = toJsonObject(container);
+        final JsonObject map = json.getJsonObject("map");
+        final JsonObject questionKeyMap = json.getJsonObject("questionKeyMap");
+        final JsonObject questionValueMap = json.getJsonObject("questionValueMap");
+
+        Supplier<String> errorMessage = () -> String.format("Expected value not found in %s", map);
+        Assertions.assertEquals("SERIALIZED:value1", map.getString("key1"), errorMessage);
+        Assertions.assertEquals("SERIALIZED:value2", map.getString("key2"), errorMessage);
+
+
+        errorMessage = () -> String.format("Expected value not found in %s", questionKeyMap);
+        Assertions.assertEquals("value1", questionKeyMap.getString("qKey1"), errorMessage);
+        Assertions.assertEquals("value2", questionKeyMap.getString("qKey2"), errorMessage);
+
+
+        errorMessage = () -> String.format("Expected value not found in %s", questionValueMap);
+        Assertions.assertEquals("qValue1", questionValueMap.getString("key1"), errorMessage);
+        Assertions.assertEquals("qValue2", questionValueMap.getString("key2"), errorMessage);
+    }
+
+    @Test
+    public void testTypeSerializerOnListElements() {
+        final ListContainer container = new ListContainer(List.of(
+                new TestImpl("value1"),
+                new TestImpl("value2")
+        ), List.of("qValue1", "qValue2"));
+
+        final JsonObject json = toJsonObject(container);
+        final JsonArray list = json.getJsonArray("list");
+        final JsonArray questionList = json.getJsonArray("questionList");
+
+        Supplier<String> errorMessage = () -> String.format("Expected value not found in %s", list);
+        Assertions.assertEquals(2, list.size(), () -> String.format("Expected a size of 2 in %s", list));
+        Assertions.assertEquals("SERIALIZED:value1", list.getString(0), errorMessage);
+        Assertions.assertEquals("SERIALIZED:value2", list.getString(1), errorMessage);
+
+        errorMessage = () -> String.format("Expected value not found in %s", questionList);
+        Assertions.assertEquals(2, questionList.size(), () -> String.format("Expected a size of 2 in %s", questionList));
+        Assertions.assertEquals("qValue1", questionList.getString(0), errorMessage);
+        Assertions.assertEquals("qValue2", questionList.getString(1), errorMessage);
+    }
+
+    @Test
+    public void testTypeSerializerOnArrayElements() {
+        final ArrayContainer container = new ArrayContainer(new TestInterface[] {
+                new TestImpl("value1"),
+                new TestImpl("value2")
+        });
+
+        final String json = jsonb.toJson(container);
+
+        Assertions.assertEquals("{\"array\":[\"SERIALIZED:value1\",\"SERIALIZED:value2\"]}", json);
+    }
+
+    @Test
+    public void testTypeSerializerOnOptionalValue() {
+        final OptionalContainer container = new OptionalContainer(Optional.of(new TestImpl("value1")), Optional.of("value2"));
+
+        final JsonObject json = toJsonObject(container);
+
+        Assertions.assertEquals("SERIALIZED:value1", json.getString("optional"));
+        Assertions.assertEquals("value2", json.getString("questionOptional"));
+    }
+
+    private JsonObject toJsonObject(final Object object) throws JsonbException {
+        final String value = jsonb.toJson(object);
+        try (
+                StringReader reader = new StringReader(value);
+                JsonReader jsonReader = Json.createReader(reader)
+        ) {
+            return jsonReader.readObject();
+        }
+    }
+}