1

Can a single custom serializer be used as both key serializer and normal object serializer?

i.e. sometime use the serializer as a key serializer for serializing map keys while at other time serializing the object normally.

I was facing issues with JsonGenerator object passed to the serializer method.

When used as a key serializer it expects a field name but when using normally, it expects the value.

amanmehara
  • 134
  • 1
  • 8
  • Try the following link which can probably has your answer.https://stackoverflow.com/questions/8367312/serializing-with-jackson-json-getting-no-serializer-found?rq=1 – Ravi Mengar Oct 01 '19 at 06:32

1 Answers1

1

You can use the same custom serializer but you need to distinguish somehow whether you need to generate property or whole object. Map is serialized to JSON Object where Map keys are converted to JSON Object properties. To generate property with Jackson we need to use writeFieldName method. To distinguish how you would like to use this serialiser in constructor you can provide this information. Example:

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import java.io.IOException;
import java.util.Collections;

public class JsonApp {

    public static void main(String[] args) throws Exception {
        SimpleModule userModule = new SimpleModule();
        userModule.addSerializer(User.class, new UserJsonSerializer(false));
        userModule.addKeySerializer(User.class, new UserJsonSerializer(true));

        ObjectMapper mapper = new ObjectMapper();
        mapper.enable(SerializationFeature.INDENT_OUTPUT);
        mapper.registerModule(userModule);

        User user = new User();
        System.out.println(mapper.writeValueAsString(Collections.singletonMap(user, user)));
    }
}

class User {

    private String firstName = "Tom";
    private String lastName = "Smith";

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
}

class UserJsonSerializer extends JsonSerializer<User> {

    private final boolean generateKey;

    UserJsonSerializer(boolean generateKey) {
        this.generateKey = generateKey;
    }

    @Override
    public void serialize(User value, JsonGenerator gen, SerializerProvider serializers)
        throws IOException {
        if (generateKey) {
            serializeAsMapKey(value, gen);
        } else {
            serializeAsObject(value, gen);
        }
    }

    private void serializeAsMapKey(User value, JsonGenerator gen) throws IOException {
        gen.writeFieldName(String.join(",", value.getFirstName(), value.getLastName()));
    }

    private void serializeAsObject(User value, JsonGenerator gen) throws IOException {
        gen.writeStartObject();
        gen.writeFieldName("first");
        gen.writeString(value.getFirstName());
        gen.writeFieldName("last");
        gen.writeString(value.getLastName());
        gen.writeEndObject();
    }
}

Above code prints:

{
  "Tom,Smith" : {
    "first" : "Tom",
    "last" : "Smith"
  }
}

If you do not have any common logic you can just create two separate classes: UserJsonSerializer and UserKeyJsonSerializer which is an object oriented and clear solution.

Michał Ziober
  • 37,175
  • 18
  • 99
  • 146
  • Thanks for the response. But I was looking for some direct API provided by Jackson. I am more interested in it because I want the same serialization logic for key and object. I have a complex object which happens to contain this object. While creating the key serializer of that complex object I wanted to use the key serializer of this object. – amanmehara Oct 03 '19 at 19:47
  • @amanmehara, I'm not sure it is possible to generate key partially. Creating key value which will represent complex object should be decoupled from `JSON` serialisation. For example: if you have class `A` and class `B` and `A has B` as one of properties you can implement `convertToKey` method for these two classes. Now in class `A` you can call `b.convertToKey()` and use it to create key for class `A`. When you have this implemented you can implement `AKeySerialiser` for class `A` and implement it like `gen.writeFieldName(a.convertToKey());`. Key generation we delegate to object. – Michał Ziober Oct 03 '19 at 22:33