1

I have a class that needs to be serialized

public class Abc
{
 private long age;
 private JaxBElement<Foo> fooWrapper;
 // other properties
}

The expected output JSON is

{
 "age": 24,
 "my_own_key": "my_own_value" // the key should not be "fooWrapper"

A constraint is that the original class Abc cannot be modified since it is generated out of xjc and I don't want to explore custom class using bindings yet.

I have tried custom serializers, bean modifiers etc. for the JaxBElement and all of them allow me to control the serialization. But they work at the VALUE of the property only. They don't allow me to change stuff at the "KEY-VALUE" level. This is the crux of the question. The key is already written out for the property before the custom serializer is invoked to control the value.

E.g. My custom serializer is invoked only after the Jackson system has emitted out the key

 "fooWrapper": // now for the value part, let me invoke the custom serializer 

So the output JSON always contains the "fooWrapper" key.

{ "fooWrapper": { "any-key": "any-value" } }
// the fooWrapper is already emitted out. That is what needs to be controlled.

My ask is to control the serialization at a higher level, such that both the key and value can be controlled. So when class Abc is being serialized, the fooWrapper property should not be written as a key at all and some custom serializer should be invoked.

Another constraint is that there are several classes like Abc which may have such JaxBElement. It is not known ahead of time. So there needs to be a generic way to attach the custom serializer.

The pseudo ask is really that we be able to attach a custom serializer to any class which has a property that matches a pattern such that the serializer can control the name of the property (or the whole key-value blob) written out.

Also, the problem is not specific to JaxBElement per se. It could be any property. The problem is more about controlled serialization INCLUDING the key being written out.

Raja Nadar
  • 9,409
  • 2
  • 32
  • 41

2 Answers2

0

Maybe you just use the incorrect kind of Serializer. This post, although a bit old should show you how to do what you want with StdSerializer.

This kind of serializer allows you to control both the key and the value.

Boris Strandjev
  • 46,145
  • 15
  • 108
  • 135
  • thanks @Boris. The above serializer allows me to control the key name of the "value object (foo)", not the 'fooWrapper' field. E.g. It ends up as { "fooWrapper": { "my_own_key": "my_own_value" } } – Raja Nadar Apr 22 '20 at 06:57
  • I would expect that you will need to define a custom Serializer for `fooWrapper` type rather than for the particular property – Boris Strandjev Apr 22 '20 at 10:56
  • The serialize was for fooWrapper. The Jackson system wrote the fooWrapper key and then invoked the serialize with the instance of fooWrapper, which is JaxBElement. So whatever I do with this object, the fooWrapper key is already written. – Raja Nadar Apr 23 '20 at 04:55
  • Can you please add the code of the serializer and deserializer you use, so that we can try to point you at improvements? – Boris Strandjev Apr 23 '20 at 07:52
0

If you want to control serialisation of key-value pair you need to register custom serialiser not only for JaxBElement<Foo> fooWrapper but also for Abc class to change a key value.

Since it is not a generic solution you can also try to create MixIn class or interface and provide extra configuration:

interface MixInA {

    @JsonSerialize(using = JAXBElementJsonSerializer.class)
    @JsonProperty("newProperty")
    JAXBElement<Foo> getFooWrapper();
}

See also:

Downside of this solution is you have to find all types for which you have to register MixIn class or interface. In case fields are different you need to create many different getters or many different MixIn interfaces to cover them all.

So, probably them most flexible solution would be to implement custom com.fasterxml.jackson.databind.AnnotationIntrospector and for given type you can return custom serialiser and custom name. Simple example:

class DynamicJaxbAnnotationIntrospector extends AnnotationIntrospector {

    @Override
    public Version version() {
        return new Version(1, 0, 0, "Dynamic JaxbElement", "your.package", "jackson.dynamic.jaxb");
    }

    @Override
    public Object findSerializer(Annotated am) {
        if (am.getRawType() == JAXBElement.class) {
            return new JAXBElementJsonSerializer();
        }
        return super.findSerializer(am);
    }

    @Override
    public PropertyName findNameForSerialization(Annotated a) {
        if (a.getRawType() == JAXBElement.class) {
            return new PropertyName("newProperty");
        }
        return super.findNameForSerialization(a);
    }
}

See also below article how to use it:

Michał Ziober
  • 37,175
  • 18
  • 99
  • 146
  • thanks @Michal. Seem to be hitting 2 issues. 1) the property name needs to be dynamic based on the jaxb field name. There is no instance info in (Annotated a) 2) The 'JAXBElementJsonSerializer' still cannot write a "value only". It has to give its own key as well. Let me know. Maybe I am still missing something. I basically want a private JaxBElement animal; field to be jsonized as { "dog": { "gem": true, "loyal": true } } } – Raja Nadar Apr 22 '20 at 08:27
  • @RajaNadar, in `MixIn` example we have two annotations: `JsonSerialize` to define serialisation for `value` and `JsonProperty` to define new `key`. Could you show class you have right now with all annotations and what do you expect? How many these classes do you have? What is the difference between them? How did you get `dog` key in above example? – Michał Ziober Apr 22 '20 at 13:26