assylias assylias - 2 months ago 11
Java Question

(De-)Serialize Bean in a custom way at runtime

Let's imagine I have the following POJO:

class Pojo {
String s;
Object o;
Map<String, String> m;

And at runtime, I want default serialization / deserialization for all properties except one. Typically, I want to replace a field by its ID in a database when serializing, similarly to this other question.

For example, I want to replace
by a string obtained from an external mapping (for example:
<=> "123" and
<=> "456"):

  • serialization: read
    and replace (so if
    , serialize as string "123")

  • deserialization: read "123", query some table to get the original value of
    back (i.e.
    ), recreate a
    object with
    o = object1

I understand that Modules would be one way to do that but I'm not sure how to use them while keeping the automatic BeanSerializer/Deserializer for the properties that don't need to be changed.

Can someone give an example (even contrived) or an alternative approach?


  • I can't use annotations or Mixins as the changes are unknown at compile time (i.e. any properties might be changed in a way that is not determinable).

  • This other question points to using a CustomSerializerFactory, which seems to do the job. Unfortunately, the official site indicates that it is not the recommended approach any more and that modules should be used instead.


To be a little clearer, I can do the following with Mixins for example:

ObjectMapper mapper = new ObjectMapper(MongoBsonFactory.createFactory());
mapper.addMixInAnnotations(Pojo.class, PojoMixIn.class);

ObjectReader reader = mapper.reader(Pojo.class);
DBEncoder dbEncoder = DefaultDBEncoder.FACTORY.create();
OutputBuffer buffer = new BasicOutputBuffer();
dbEncoder.writeObject(buffer, o);

with the following Mixin:

abstract class PojoMixIn {
@JsonIgnore Object o;

And then add the required string to the JSON content. But I would need to know at compile time that it is the
field that needs to be replaced, which I don't.


I think @JsonSerialize and @JsonDeserialize is what you need. These annotations give you control on the serialization/deserialization of particular fields. This question shows elegant way to combine them into one annotation.

UPD. For this complex scenario you could take a look at BeanSerializerModifier/BeanDeserializerModifier classes. The idea is to modify general BeanSerializer/BeanDeserializer with your custom logic for particular fields and let basic implementation to do other stuff. Will post an example some time later.

UPD2. As I see, one of the way could be to use changeProperties method and assign your own serializer.

UPD3. Updated with working example of custom serializer. Deserialization could be done in similar way.

UPD4. Updated example with full custom serialization/deserialization. (I have used jakson-mapper-asl-1.9.8)

  public class TestBeanSerializationModifiers {

    static final String PropertyName = "customProperty";
    static final String CustomValue = "customValue";
    static final String BaseValue = "baseValue";

    // Custom serialization

    static class CustomSerializer extends JsonSerializer<Object> {
        public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
            String customValue = CustomValue; // someService.getCustomValue(value);

    static class MyBeanSerializerModifier extends BeanSerializerModifier {
        public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BasicBeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
            for (int i = 0; i < beanProperties.size(); i++) {
                BeanPropertyWriter beanPropertyWriter = beanProperties.get(i);
                if (PropertyName.equals(beanPropertyWriter.getName())) {
                    beanProperties.set(i, beanPropertyWriter.withSerializer(new CustomSerializer()));
            return beanProperties;

    // Custom deserialization

    static class CustomDeserializer extends JsonDeserializer<Object> {
        public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
            // serialized value, 'customValue'
            String serializedValue = jp.getText();
            String baseValue = BaseValue; // someService.restoreOldValue(serializedValue);
            return baseValue;

    static class MyBeanDeserializerModifier extends BeanDeserializerModifier {
        public BeanDeserializerBuilder updateBuilder(DeserializationConfig config, BasicBeanDescription beanDesc, BeanDeserializerBuilder builder) {
            Iterator<SettableBeanProperty> beanPropertyIterator = builder.getProperties();
            while (beanPropertyIterator.hasNext()) {
                SettableBeanProperty settableBeanProperty =;
                if (PropertyName.equals(settableBeanProperty.getName())) {
                    SettableBeanProperty newSettableBeanProperty = settableBeanProperty.withValueDeserializer(new CustomDeserializer());
                    builder.addOrReplaceProperty(newSettableBeanProperty, true);
            return builder;

    static class Model {

        private String customProperty = BaseValue;
        private String[] someArray = new String[]{"one", "two"};

        public String getCustomProperty() {
            return customProperty;

        public void setCustomProperty(String customProperty) {
            this.customProperty = customProperty;

        public String[] getSomeArray() {
            return someArray;

        public void setSomeArray(String[] someArray) {
            this.someArray = someArray;

    public static void main(String[] args) {
        SerializerFactory serializerFactory = BeanSerializerFactory
                .withSerializerModifier(new MyBeanSerializerModifier());

        DeserializerFactory deserializerFactory = BeanDeserializerFactory
                .withDeserializerModifier(new MyBeanDeserializerModifier());

        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setDeserializerProvider(new StdDeserializerProvider(deserializerFactory));

        try {
            final String fileName = "test-serialization.json";
            // Store, "customValue" -> json
            objectMapper.writeValue(new File(fileName), new Model());
            // Restore, "baseValue" -> model
            Model model = objectMapper.readValue(new File(fileName), Model.class);
        } catch (IOException e) {