eclipse-ee4j / eclipse-ee4j/yasson

Serialization of a Map fails if the key uses a custom Serializer

Open
#500 1 comment 0 reactions 0 assignees View on GitHub
bug
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.