Curious exception for key serialization problem

41 views
Skip to first unread message

Benson Margulies

unread,
Nov 5, 2014, 11:50:17 AM11/5/14
to jackso...@googlegroups.com
2.4.2.

I've registered a mixin for an enum. Reading and writing the enum as a value works find. Reading it as a map key works fine. Writing it, not so much. I guess that I need a specific key serializer, but this seems an interesting way to tell me.

com.fasterxml.jackson.databind.JsonMappingException: com.basistech.rosette.dm.LanguageCodeSerializer cannot be cast to com.fasterxml.jackson.databind.ser.std.StdSerializer
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:125)
at com.fasterxml.jackson.databind.ObjectMapper._configAndWriteValue(ObjectMapper.java:2866)
at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(ObjectMapper.java:2323)
at com.basistech.rosette.dm.EnumModuleTest.languageCodeKey(EnumModuleTest.java:55)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:67)
Caused by: java.lang.ClassCastException: com.basistech.rosette.dm.LanguageCodeSerializer cannot be cast to com.fasterxml.jackson.databind.ser.std.StdSerializer
at com.fasterxml.jackson.databind.ser.std.EnumMapSerializer.serializeContents(EnumMapSerializer.java:225)
at com.fasterxml.jackson.databind.ser.std.EnumMapSerializer.serialize(EnumMapSerializer.java:182)
at com.fasterxml.jackson.databind.ser.std.EnumMapSerializer.serialize(EnumMapSerializer.java:29)
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:114)
... 25 more

Tatu Saloranta

unread,
Nov 5, 2014, 12:40:42 PM11/5/14
to jackso...@googlegroups.com
Sounds like a bug -- it did find your serializer, but for some reason assumed it should be a sub-type of StdSerializer (instead of generic JsonSerializer). I can have a look at that, but you may be able to work around the issue by sub-classing StdSerializer for your custom serializer in the meantime.

-+ Tatu +-

--
You received this message because you are subscribed to the Google Groups "jackson-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jackson-user...@googlegroups.com.
To post to this group, send email to jackso...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Tatu Saloranta

unread,
Nov 5, 2014, 12:42:41 PM11/5/14
to jackso...@googlegroups.com
And looking at source, commentary there says:

            if (keyEnums == null) {
                /* 15-Oct-2009, tatu: This is clumsy, but still the simplest efficient
                 * way to do it currently, as Serializers get cached. (it does assume we'll always use
                 * default serializer tho -- so ideally code should be rewritten)
                 */
                // ... and lovely two-step casting process too...
                StdSerializer<?> ser = (StdSerializer<?>) provider.findValueSerializer(
                        key.getDeclaringClass(), _property);
                keyEnums = ((EnumSerializer) ser).getEnumValues();
            }

So yes, it is another case where combination of JSON key/property name handling, and Enum handling collide.

And as to work-around, extending StdSerializer probably won't do much good here, so disregard that advice.

-+ Tatu +-

Benson Margulies

unread,
Nov 5, 2014, 12:46:41 PM11/5/14
to jackso...@googlegroups.com
I made you an issue with a test case.

So I stopped using an EnumMap and retreated to a HashMap, and now my problem is that my custom key serializer is ignored. I've tried both using addKeySerializers in the module and keyUsing in the @JsonSerialize in my mixin for this enum.

Tatu Saloranta

unread,
Nov 5, 2014, 6:27:55 PM11/5/14
to jackso...@googlegroups.com
Thank you. I can see how and where EnumMapSerializer fails. It needs a... significant rewrite, but that may need to go in 2.5 (depending on how much its API is to change). Or maybe not.

But general (Hash)Map failure is stranger; that should be tested and working.

-+ Tatu +-

Tatu Saloranta

unread,
Nov 5, 2014, 6:33:44 PM11/5/14
to jackso...@googlegroups.com
On Wed, Nov 5, 2014 at 9:46 AM, Benson Margulies <ben...@basistech.com> wrote:
I made you an issue with a test case.

So I stopped using an EnumMap and retreated to a HashMap, and now my problem is that my custom key serializer is ignored. I've tried both using addKeySerializers in the module and keyUsing in the @JsonSerialize in my mixin for this enum.


Ok. Reading this more closely, this may be due to usage. I will try to reproduce the issue with `addKeySerializer()` (which should work). But `keyUsing` is something that needs to be done for `Map` in question, and not for enum. So either declaration for (custom sub-classed) Map, or mix-in for Map.class, or for Map-valued property. But annotation on Enum itself won't be checked for this use case.

Benson Margulies

unread,
Nov 5, 2014, 7:15:34 PM11/5/14
to jackso...@googlegroups.com
Yes. I really hoped that I could change how an enum was serialized _when used as a key_ by annotating the key.

Tatu Saloranta

unread,
Nov 7, 2014, 6:26:09 PM11/7/14
to jackso...@googlegroups.com
Hmmh. Funny how intuitive that'd be, considering I didn't consider it back in the day.

But perhaps it would be possible to add this as an option.
I am not sure if it would be confusing to overload current semantics ("if X is a Map, then it means key-(de)ser for entries; if not, it is when X is used as a key") -- it could not be done for 'contentUsing', but since Maps really won't work well as keys, it might work here.
Or, alternatively, just adding one more property... but not sure what kind of name would be intuitive ("whenUsedAsKeyUsing" is... not good... maybe just "keySerializer"?).
Perhaps overloading would actually be less confusing here.

-+ Tatu +-

Benson Margulies

unread,
Nov 7, 2014, 6:30:09 PM11/7/14
to jackso...@googlegroups.com

Note that the enum case somehow already goes down this path, using the ordinary serialization property.

Tatu Saloranta

unread,
Nov 7, 2014, 7:36:59 PM11/7/14
to jackso...@googlegroups.com
What do you mean?

-+ Tatu +-

Benson Margulies

unread,
Nov 7, 2014, 10:36:18 PM11/7/14
to jackso...@googlegroups.com
On Fri, Nov 7, 2014 at 7:36 PM, Tatu Saloranta <ta...@fasterxml.com> wrote:
> What do you mean?

This all started when I:

a: created a module that registered a serializer for an enum
b: serialized a root EnumMap
c: watched the EnumMap serializer _try_ to respect the registered
serializer for the type, even though it was never supplied as a
keyUsing anywhere.

I guess I've lost the thread of why the special 'key' option exists.

Tatu Saloranta

unread,
Nov 9, 2014, 12:22:15 AM11/9/14
to jackso...@googlegroups.com
On Sat, Nov 8, 2014 at 3:36 AM, Benson Margulies <ben...@basistech.com> wrote:
On Fri, Nov 7, 2014 at 7:36 PM, Tatu Saloranta <ta...@fasterxml.com> wrote:
> What do you mean?

This all started when I:

  a: created a module that registered a serializer for an enum
  b: serialized a root EnumMap
  c: watched the EnumMap serializer _try_ to respect the registered
serializer for the type, even though it was never supplied as a
keyUsing anywhere.

I guess I've lost the thread of why the special 'key' option exists.



Right. That usage should and will work, after fix for #601 goes in (which will be in today).

But I thought that it would be a nice alternative to allow fourth custom serializer setting for a type. Right now, Map types can specify 3 custom serializers:

1. Serializer for Map itself
2. Serializer for values of the Map
3. Serializer for keys of the Map

but for scalar types, only option (1) is available; and for Collections/arrays, first two.

However, it would be useful to be able to specify fourth option, "serializer to use when a value of this type is _used as key_' -- this is different from (3) which means "serializer to use for keys contained in this data structure".

And one possibility would be to augment meaning of existing "JsonSerialize.keyUsing" to have different semantics for Map types, and other (scalar) types.

-+ Tatu +-
Reply all
Reply to author
Forward
0 new messages