eclipse-ee4j / eclipse-ee4j/yasson
Serialization of a Map fails if the key uses a custom Serializer
- Dominant language
- Java
- Stars
- 218
- Forks
- 109
- Avg merge
- 1d 5h
- Merged PRs (30d)
- 9
Description
**Describe the bug**
Define a custom Serializer for a Locale object like the following:
```java
public static class LocaleSerializer implements JsonbSerializer {
@Override
public void serialize(Locale obj, JsonGenerator generator, SerializationContext ctx) {
generator.write(obj.toLanguageTag());
}
}
```
And use a Pojo class that contains a Map field with Locale as the key:
```java
public class TestObject {
private Map localeKeyMap;
...
}
```
If you try to serialize it with current version 2.0.2 like the following:
```java
Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()
.withSerializers(new LocaleSerializer())
.withDeserializers(new LocaleDeserializer()));
TestObject o = new TestObject();
o.getLocaleKeyMap().put(Locale.US, "us");
jsonb.toJson(o);
```
The following exception is thrown:
```
jakarta.json.bind.JsonbException: Unable to serialize property 'values' from test.MapObjectTest.MapObjectLocaleString
at org.eclipse.yasson.internal.serializer.ObjectSerializer.serializeInternal(ObjectSerializer.java:71)
at org.eclipse.yasson.internal.serializer.AbstractContainerSerializer.serialize(AbstractContainerSerializer.java:107)
at org.eclipse.yasson.internal.Marshaller.serializeRoot(Marshaller.java:147)
at org.eclipse.yasson.internal.Marshaller.marshall(Marshaller.java:73)
at org.eclipse.yasson.internal.Marshaller.marshall(Marshaller.java:101)
at org.eclipse.yasson.internal.JsonBinding.toJson(JsonBinding.java:126)
at test.MapObjectTest.test1(MapObjectTest.java:95)
...
Caused by: java.lang.ClassCastException: java.lang.String cannot be cast to java.util.Locale
at test.MapObjectTest$LocaleSerializer.serialize(MapObjectTest.java:27)
at org.eclipse.yasson.internal.serializer.UserSerializerSerializer.serialize(UserSerializerSerializer.java:53)
at org.eclipse.yasson.internal.serializer.AbstractContainerSerializer.serializerCaptor(AbstractContainerSerializer.java:125)
at org.eclipse.yasson.internal.serializer.AbstractContainerSerializer.serializeItem(AbstractContainerSerializer.java:194)
at org.eclipse.yasson.internal.serializer.MapToEntriesArraySerializer.lambda$serializeContainer$0(MapToEntriesArraySerializer.java:111)
at java.util.HashMap.forEach(HashMap.java:1289)
at org.eclipse.yasson.internal.serializer.MapToEntriesArraySerializer.serializeContainer(MapToEntriesArraySerializer.java:106)
at org.eclipse.yasson.internal.serializer.MapSerializer.serializeInternal(MapSerializer.java:156)
at org.eclipse.yasson.internal.serializer.MapSerializer.serializeInternal(MapSerializer.java:27)
at org.eclipse.yasson.internal.serializer.AbstractContainerSerializer.serialize(AbstractContainerSerializer.java:107)
at org.eclipse.yasson.internal.serializer.AbstractContainerSerializer.serializerCaptor(AbstractContainerSerializer.java:125)
at org.eclipse.yasson.internal.serializer.ObjectSerializer.marshallProperty(ObjectSerializer.java:121)
at org.eclipse.yasson.internal.serializer.ObjectSerializer.serializeInternal(ObjectSerializer.java:69)
... 38 more
```
**To Reproduce**
With the attached maven project just execute test on it:
```bash
unzip issue-map-locale-key.zip
cd issue-map-locale-key
mvn clean test
```
**Expected behavior**
The test should pass and the LocaleSerializer should be used to serialize the Locale key into a String.
**System information:**
- OS: any
- Java Version: 8 or 11
- Yasson Version: 2.0.2
**Additional context**
The problem can be that the _MapToEntriesArraySerializer_ calls to serialize the key and the values with the same method [MapSerializer.serializeItem](https://github.com/eclipse-ee4j/yasson/blob/2.0.2/src/main/java/org/eclipse/yasson/internal/serializer/MapToEntriesArraySerializer.java#L109-L111). The _serializeItem_ method is in _AbstractContainerSerializer_ and this class always obtains the [getValueType](https://github.com/eclipse-ee4j/yasson/blob/2.0.2/src/main/java/org/eclipse/yasson/internal/serializer/AbstractContainerSerializer.java#L206) from the generic 0 (the key). In a map the generic type can be 0 (the key) or 1 (the value). We are preparing a tentative PR for review. We will try to submit it as soon as possible.
Reproducer project: [issue-map-locale-key.zip](https://github.com/eclipse-ee4j/yasson/files/6782335/issue-map-locale-key.zip)
Contributor guide
Research direction
Start with MapToEntriesArraySerializer and AbstractContainerSerializer, especially the serializeItem and getValueType paths described in the issue. Run the attached reproducer with mvn clean test from issue-map-locale-key, then verify that the LocaleSerializer is used for the map key and the test passes.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 50/100