9

I want to add a custom serializer and deserializer for JSR 363 javax.measure.Quantity<Q extends Quantity<Q>>, which basically encapsulates a "value" and a "unit". Creating the serializer (extends JsonSerializer<Quantity<?>>) and the deserializer (extends StdDeserializer<Quantity<?>>) is easy.

But it's not easy to register them. For deserializers, it's OK; look at the signature:

SimpleModule.addDeserializer(Class<T> type, JsonDeserializer<? extends T> deser)

Notice that the deserializer allows the generic type to be extended. So I can do this:

module.addDeserializer(Quantity.class, new MyQuantityJsonDeserializer());

But the serializer is another story; look at the signature for its registration:

SimpleModule.addSerializer(Class<? extends T> type, JsonSerializer<T> ser)

Oh, the pain this causes because of the restricted generic type. I cannot do this:

module.addSerializer(Quantity.class, new MyQuantityJsonSerializer());

This is because Quantity.class will never give me a Class<Quantity<Q extends Quantity<Q>>. And there is no easy cast around this, without introducing some arbitrary generic variable in the module method and using acrobatic casts. Even this won't work:

module.addSerializer((Class<Quantity<?>>) Quantity.class, new MyQuantityJsonSerializer());

The best I've been able to do is to make my serializer class QuantityJsonSerializer<Q extends Quantity<Q>> extends JsonSerializer<Q>, and then in the module register it as the raw type:

@SuppressWarnings({"rawtypes", "unchecked"})
final JsonSerializer<Quantity> quantitySerializer =
    (JsonSerializer<Quantity>) new MyQuantityJsonSerializer();
module.addSerializer(Quantity.class, quantitySerializer);

Ugh, how ugly is that? But that is one of the only ways I can find to even get it to work, and I had to go through more gymnastics to remove the warnings.

Surely SimpleModule.addSerializer() could have been a bit more flexible on its generic parameters, analogous to SimpleModule.addDeserializer()?

I'm reporting this here because the Jackson project said to report bugs here --- and I'm also asking for better workaround ideas. Thanks.

Garret Wilson
  • 18,219
  • 30
  • 144
  • 272
  • Do you need to use generics? – mikep Jun 27 '17 at 02:00
  • You can cast the class with a two-part cast, something like `(Class>) (Class extends Quantity>) Quantity.class` or `(Class>) (Class>) Quantity.class`. The design of `Class` is really just quite bad for this kind of thing. Java needs to replace it with something like [`TypeToken`](http://google.github.io/guava/releases/snapshot/api/docs/com/google/common/reflect/TypeToken.html). The problems won't go away as long as it's useful to register some kind of handler with some particular type. The answer by ck1 suggests you don't need to do the cast in this case, though. – Radiodef Jun 27 '17 at 04:07
  • But what about the inconsistency in the `addSerializer()` and `addDeserializer()` methods? Why is there an inconsistency? Where do I file an issue so that Jackson can fix this? Or is there a reason the inconsistency should remain? – Garret Wilson Jun 27 '17 at 16:46

1 Answers1

7

In SimpleModule there is an overloaded version of addSerializer():

public SimpleModule addSerializer(JsonSerializer<?> ser)

which allows this to work:

module.addSerializer(new MyQuantityJsonSerializer());

as long as you define your serializer as:

public class MyQuantityJsonSerializer extends StdSerializer<Quantity<?>> {
    public MyQuantityJsonSerializer() {
        // Note: second argument here is ignored.
        super(Quantity.class, false);
    }

    @Override
    public void serialize(Quantity<?> value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        // Serialize value here...
    }
}
ck1
  • 5,243
  • 1
  • 21
  • 25