1

I have a class that belongs to a third-party library that I don't have access to. It looks like:

public class Product {

    private String thumbnailUrl;    

   // many other properties, some of which are POJOs or collections

    public String getThumbnailUrl() {
        return thumbnailUrl;
    }

    public void setThumbnailUrl(String thumbnailUrl) {
        this.thumbnailUrl = thumbnailUrl;
    }
}

This is only one of the classes that I am going to serialize into string using Jackson. There are quite a few others.

The thumbnailUrl property can sometimes be null. How can I configure an ObjectMapper to serialize this field as http://www.example.com/unknown.png, if it is null?

I think I cannot use mixins as mixins won't have access to the original object hence they can't check if the value of the property is null or not.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Outkast
  • 153
  • 4

2 Answers2

0

This should do the trick:

private String thumbnailUrl = "http://my.default.url";

As the OP mentioned in the comments, the class comes from a third-party library, so modifying it (a'la this SO) or annotating (a'la this SO) is not an option. In this case you have two options left:

  • Patch the third-party lib (apparently you have access to the source ;))

  • Hack your way around it in the custom serializer (needs to be properly tested though, and this quick hack wouldn't work in multi-threaded environments)

As in:

@Override
public void serialize(Product value, JsonGenerator jgen, SerializerProvider provider) 
  throws IOException, JsonProcessingException {
    boolean revert = false;
    if (value.thumbnailUrl == null) {
      value.thumbnailUrl = "http://my.default.url";
      revert = true;
    }
    jgen.writeObject(value);
    if (revert) {
      value.thumbnailUrl = null;
    }
}
Community
  • 1
  • 1
D. Kovács
  • 1,232
  • 13
  • 25
  • As mentioned in the question, this is a third party class and I don't have access to the source code. I want to externalize this logic. – Outkast Jan 11 '17 at 13:56
  • Then I'm afraid you'll have to write your own serializer for this class. There are numerous how-to for that, e.g., http://www.baeldung.com/jackson-custom-serialization of course you won't be able to annotate the class, but still can configure your ObjectMapper to use your custom serializer for the class (also in the linked how-to) – D. Kovács Jan 11 '17 at 13:59
  • I can write a custom serializer, but I want all aspects of serialization to remain as is, and only customize serialization of this field. If I write a custom serializer for a POJO that has 20 fields and add serialization logic for all the 20 fields, that would be terrible. – Outkast Jan 11 '17 at 14:08
  • Yes, but you could hack it around, I'll update my answer, give me a couple minutes please. – D. Kovács Jan 11 '17 at 14:12
  • This won't work in my case, as changing value of the thumbnail property will have unwanted side effects (e.g. listeners getting triggered, etc.) I am marking this answer as accepted though (until someone comes up with a better solution). – Outkast Jan 12 '17 at 11:02
  • As a hack-hack you could do a (deep)copy of the object being serialized, and then listeners won't be triggered because the original object won't be changed. – D. Kovács Jan 12 '17 at 11:37
0

I ended up with this solution but it is not as simple as I was expecting it to be:

@Test
public void shouldSerializeDefaultThumbnailUrlIfThumbnailUrlIsNull() throws Exception {
    // Given
    Product p = new Product();
    p.setPrimaryKey("123");

    PropertyFilter filter = new SimpleBeanPropertyFilter() {
        @Override
        public void serializeAsField(Object pojo, JsonGenerator jgen, SerializerProvider prov, PropertyWriter writer) throws Exception {
            if (pojo.getClass().equals(Product.class)
                    && ((Product) pojo).getThumbnailUrl() == null
                    && writer.getName().equals("thumbnailUrl")) {
                jgen.writeStringField(writer.getName(), "http://www.example.com/default.png");
            } else {
                writer.serializeAsField(pojo, jgen, prov);
            }
        }
    };

    FilterProvider filters = new SimpleFilterProvider().addFilter("thumbnailFilter", filter);

    this.mapper.setFilterProvider(filters);
    String json = this.mapper.writeValueAsString(p);

    assertThat(json, equalTo("{\"thumbnailUrl\":\"http://www.example.com/default.png\",\"id\":\"123\"}"));
}

Ignore this. It doesn't work in my case as my class needs to be annotated which I can't.

Outkast
  • 153
  • 4
  • If I could implement a `com.fasterxml.jackson.databind.JsonSerializer` for the `Product` class that was identical to the default serializer but had this rule only for this particular field, that would be much nicer. – Outkast Jan 11 '17 at 14:18
  • That would indeed be nice, but I haven't found a nice, easy way to do exactly that. – D. Kovács Jan 11 '17 at 15:14