Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jackson how map one Pojo field to 2 (json) fields (same content, different name)

I use Jackson to serialise POJOs into CSV. Now we need to change the naming for certain fields to snake_case. This is easily done by @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class).

For compatibility reasons we need some of the renamed fields also with their old name.

E.g.:

public class Pojo {
    private int someField;
}

Default will serialise to "someField", SnakeCaseStrategy will serialise to "some_field".

How to get serialization with both?:

{
  "someField" : "one",
  "some_field" : "one" 
}

My first try was a mixin:

public abstract class PojoFormat {

    @JsonProperty("someField")
    abstract String getSomeField();

}

but this effectively only undoes the naming strategy change. So how to copy a field in serialization - preferable not by changing the Pojo (this copied fields should be removed when all clients can cope with it).

Little update:

in my real class there some nested class that use JsonUnwrapped and the doc stated that this is not working with custom serializer (didn't know that this makes a difference here).

like image 569
dermoritz Avatar asked Oct 31 '25 09:10

dermoritz


1 Answers

Well, I have never seen this before, I would be very happy if someone here in this site knows how.

The easy way, in my opinion, is to use a Custom Serializer.

For example:

  1. Using the @JsonSerialize annotation
  2. Register a module
  3. Dynamic Serializer with Reflection

@JsonSerialize annotation

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;

@JsonSerializer(using=PojoSerializer.class)
class Pojo {
 
  private String myValue;
   
  // getters and setters

}

class PojoSerializer extends StdSerializer<Pojo> {

  public PojoSerializer() {
   super(Pojo.class);
  }

      @Override
    public void serialize(Pojo value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        gen.writeStartObject();
        gen.writeStringField("myValue", value.getMyValue());
        gen.writeStringField("my_value", value.getMyValue());
        gen.writeEndObject();

    }
}

Module

static class Pojo {

    private String myValue;

    public String getMyValue() {
        return myValue;
    }

    public Pojo setMyValue(String myValue) {
        this.myValue = myValue;
        return this;
    }
}

static class PojoSerializer extends StdSerializer<Pojo> {

    public PojoSerializer() {
        super(Pojo.class);
    }

    @Override
    public void serialize(Pojo value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        gen.writeStartObject();
        gen.writeStringField("myValue", value.getMyValue());
        gen.writeStringField("my_value", value.getMyValue());
        gen.writeEndObject();
    }
}


public static void main(String[] args) throws  JsonProcessingException {

    final ObjectMapper mapper = new ObjectMapper();

    final SimpleModule module = new SimpleModule("PojoModule");

    module.addSerializer(Pojo.class, new PojoSerializer());

    mapper.registerModule(module);

    final Pojo pojo = new Pojo();
    pojo.setMyValue("This is the value of my pojo");

    System.out.println(mapper.writeValueAsString(pojo));

}

Reflection

I write some code for you, you might want to see to get new ideias. This works as a generic way(just to not write several serializers).

// The serializer will be register in the ObjectMapper module.
static class Pojo {

    private String myValue = "With snake and camel";
    private String value = "Without snake case";
    private String thirdValue = "snake & camel";

}

// using the annotation
@JsonSerialize(using = PojoSerializer.class)
static class Pojo2 {

    private String pojoName = "Pojo 2";
    private String pojo = "pojp";

}

static class PojoSerializer extends StdSerializer<Object> {

    public PojoSerializer() {
        super(Object.class);
    }

    @Override
    public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        gen.writeStartObject();

        final Field[] fields = value.getClass().getDeclaredFields();
        for(final Field field : fields) {

            final String name = field.getName();
            final String fieldValue;
            try {
                // Do not use this!
                fieldValue = (String)field.get(value);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }

            byte firstUpperCase = -1;
            for(byte index = 0; index < name.length(); index++) {
                final char caractere = name.charAt(index);

                // A ascii code is 66 decimal, and 90 is the Z in decimal
                if(caractere > 'A' && caractere < 'Z') {
                    // found the first upper
                    firstUpperCase = index;
                    break;
                }
            }

            // writes the normal field name
            gen.writeStringField(name, fieldValue);

            // if the name is in camel case, we will write in snake case too.
            if(firstUpperCase != -1) {
                final char lowerLetter = (char)((int) name.charAt(firstUpperCase) + 32);
                final String left = name.substring(0, firstUpperCase);
                final String right = String.format("%c%s",lowerLetter, name.substring(firstUpperCase + 1));
                gen.writeStringField(String.format("%s_%s", left, right), fieldValue);

            }
        }
        gen.writeEndObject();
    }
}
like image 181
sgtcortez Avatar answered Nov 01 '25 22:11

sgtcortez



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!