javajsonjacksonjson-deserialization

Jackson serialize simple one attribute ValueObject like Enum - without nesting


Is it possible to configure jackson to serialize simple value objects, wrapping only one attribute, to be serialized like enums?

public final class ErrorCode {

  private final String value;

  public ErrorCode(@JsonProperty("value") final String value) {
    this.value = value;
  }

  public String getValue() {
    return value;
  }
}

Now it is serialized as ..., "errorCode":{"value":"invalid.login"}, ... but I would like to have, as if it were enum, ..., "errorCode":"invalid.login", .... It is possible to but only via @JsonUnwrapped() in each surrounding class

SorroundingClass {
  @JsonUnwrapped()
  private ErrorCode errorCode;  
  ...
}

I would like to configure It only in one place, best in ErrorCode itself.

Looking at flattening-nested-attributes-in-jackson it seems to me impossible, but I want to make sure.


Solution

  • You can achieve this by implementing custom com.fasterxml.jackson.databind.JsonSerializer serializer:

    class SingleValueJsonSerializer extends JsonSerializer<Object> {
    
        @Override
        public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
            if (value == null) {
                gen.writeNull();
                return;
            }
            final Field[] fields = value.getClass().getDeclaredFields();
            if (fields.length != 1) {
                throw new IOException("Do not use this serialiser for the class " + value.getClass().getSimpleName() + " which has" + fields.length + " fields!");
            }
            final Field first = fields[0];
            try {
                final Object fieldValue = first.get(value);
                gen.writeObject(fieldValue);
            } catch (IllegalAccessException e) {
                throw new IOException(e);
            }
        }
    }
    

    You can define it in required class:

    class Wrapper {
    
        @JsonSerialize(using = SingleValueJsonSerializer.class)
        private ErrorCode error;
        // other fields
    }
    

    or register it globally by defining on the class level:

    @JsonSerialize(using = SingleValueJsonSerializer.class)
    class ErrorCode {