Skip to content
This repository has been archived by the owner on Nov 5, 2019. It is now read-only.

Invalidating SerializationInclusion.NON_NULL of other modules #52

Closed
natnan opened this issue Mar 2, 2015 · 10 comments
Closed

Invalidating SerializationInclusion.NON_NULL of other modules #52

natnan opened this issue Mar 2, 2015 · 10 comments
Milestone

Comments

@natnan
Copy link

natnan commented Mar 2, 2015

zapodot/jackson-databind-java-optional#2

tl;dr

If a different module with custom SerializationInclusion handling is registered after AfterBurnerModule, nulls are de/serialized still as nulls, ignoring the other module. The workaround is to

  • Register the other module first. Not possible when one uses dropwizard for example.
  • Add @JsonProperty to those specific fields. Not pretty when one has many of those.

Unit tests are available in the issue above.

@cowtowncoder
Copy link
Member

Unit test much appreciated. Usually problems like this are due to optimized handling which may not take into account all processing logic that is being added. The reason why pre-registration may work is if serializer is constructed first before afterburner has a chance to add optimized handler.

@cowtowncoder
Copy link
Member

Hmmh. Actually, it might not be that easy to see what is going on, as this appear to involve additional serializers from linked-to project. It would be good to have a stand-alone unit test that exhibits the problem only using classes declared in test class itself, to isolate the problem.

The challenge is that Afterburner does not change inclusion settings; and the code does check for inclusion. But obviously something does go wrong with optional type handling, somehow. Isolating that difference would be necessary since I can not add dependency from afterburner test to the other project.

@natnan
Copy link
Author

natnan commented Mar 3, 2015

The serializer is a bit silly but does the job :).

public class SampleObject {
  private String field1;
  private Integer field2;
  private Integer field3;

  public SampleObject(String field1, Integer field2, Integer field3) {
    this.field1 = field1;
    this.field2 = field2;
    this.field3 = field3;
  }

// getter setters
}

public class NullSerializerTest {

  @Test
  public void test() throws JsonProcessingException {
    SampleObject sampleObject = new SampleObject(null, 2, 3);

    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.registerModule(new AfterburnerModule());

    SimpleModule simpleModule = new SimpleModule();
    simpleModule.setSerializerModifier(new Only2BeanSerializerModifier());
    objectMapper.registerModule(simpleModule);
    objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    String json = objectMapper.writeValueAsString(sampleObject);

    assertEquals("{\"field2\":2}", json);
  }

  public class Only2BeanSerializerModifier extends BeanSerializerModifier {
    @Override
    public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
      return beanProperties.stream().map((w) -> {
        BeanPropertyWriter mapping = w;
        if (Integer.class.isAssignableFrom(w.getPropertyType())) {
          mapping = new Only2BeanPropertyWriter(w);
        }
        return mapping;
      }).collect(Collectors.toList());
    }
  }

  public class Only2BeanPropertyWriter extends BeanPropertyWriter {

    protected Only2BeanPropertyWriter(BeanPropertyWriter base) {
      super(base);
    }

    @Override
    public void serializeAsField(Object bean, JsonGenerator jgen, SerializerProvider prov) throws Exception {
      Object val = get(bean);
      if((val == null || !val.equals(2)) && _nullSerializer == null) {
        return;
      }
      super.serializeAsField(bean, jgen, prov);
    }
  }
}
org.junit.ComparisonFailure: 
Expected :{"field2":2}
Actual   :{"field2":2,"field3":3}

@natnan
Copy link
Author

natnan commented Mar 3, 2015

Oh wait, it failed without AfterBurner as well. Hmm..

@natnan
Copy link
Author

natnan commented Mar 4, 2015

OK, fixed it. Probably can be simplified though.

It looks like it ignores the BeanPropertyWriter when AfterBurner is registered even though BeanSerializerModifier returns it (according to my debug steps).

@cowtowncoder
Copy link
Member

Thanks -- that sounds plausible, yes. Need to see what checks need to be added to avoid optimizing this case.

cowtowncoder added a commit that referenced this issue Mar 4, 2015
@cowtowncoder cowtowncoder added this to the 2.6. milestone Mar 4, 2015
@cowtowncoder
Copy link
Member

Accidentally closed the wrong issue.

@cowtowncoder cowtowncoder reopened this Mar 4, 2015
@cowtowncoder
Copy link
Member

Ah. Just realized that a more likely explanation is the ordering of modules.

If so, not sure if this actually can be solved or not.

@cowtowncoder cowtowncoder modified the milestones: 2.5.2, 2.6. Mar 4, 2015
@cowtowncoder
Copy link
Member

Turned out there is another way to solve this problem, by blocking use of optimized BeanPropertyWriter subtype of bpw given is a custom type.

@natnan
Copy link
Author

natnan commented Mar 4, 2015

👍, thanks.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants