Different JSON output when using custom json serializer in Spring Data Rest
Beyond andreast00's solution above - make sure to also override the other constructors and with... methods as well or it may create a default UnwrappingBeanSerializer in the background and ignore your custom serialization code:
public UnwrappingOrderSerializer(UnwrappingBeanSerializer src, ObjectIdWriter objectIdWriter) {
super(src, objectIdWriter);
}
public UnwrappingOrderSerializer(UnwrappingBeanSerializer src, ObjectIdWriter objectIdWriter, Object filterId) {
super(src, objectIdWriter, filterId);
}
public UnwrappingOrderSerializer(UnwrappingBeanSerializer src, Set<String> toIgnore) {
super(src, toIgnore);
}
@Override
public BeanSerializerBase withObjectIdWriter(ObjectIdWriter objectIdWriter) {
return new UnwrappingOrderSerializer(this, objectIdWriter);
}
@Override
public BeanSerializerBase withFilterId(Object filterId) {
return new UnwrappingOrderSerializer(this, this._objectIdWriter, filterId);
}
@Override
protected BeanSerializerBase withIgnorals(Set<String> toIgnore) {
return new UnwrappingOrderSerializer(this, toIgnore);
}
Also, depending on whether the serialisation is occurring from a @JsonUnwrapped object, as an object in an array or as a single object the jsonGenerater.writeStartObject may need to be calleddepends on weather the object has been started or not. I used:
boolean writeStartEnd = !jsonGenerator.getOutputContext().inObject()
|| jsonGenerator.getOutputContext().getCurrentName() != null;
if (writeStartEnd) jsonGenerator.writeStartObject(entity);
...serialisation code...
if (writeStartEnd) {
jsonGenerator.writeEndObject();
}
Projection is one solution and overriding one method of JsonSerializer
is another:
@Override
public boolean isUnwrappingSerializer() {
return true;
}
Then you should be able to omit the start and end of an object.
Find my blog post here.
This is not a bug of Spring Data Rest it is actually the normal behaviour of the Jackson Serializer. Whenever you use the @JsonUnwrapped Annotation (as the Resource content field does) together with a custom Serializer the Jackson Serializer will explicitly write the field name (in this case content). Have a look at the UnwrappingBeanPropertyWriter for more details. Anyhow you have been on the right track using the UnwrappingBeanSerializer but the setup is slightly different then the usual Serializer registration. The following example should fix your problem:
@Override
protected void configureJacksonObjectMapper(ObjectMapper objectMapper) {
mapper.registerModule(new Module() {
@Override
public String getModuleName() {
return "my.module";
}
@Override
public Version version() {
return Version.unknownVersion();
}
@Override
public void setupModule(SetupContext context) {
context.addBeanSerializerModifier(new BeanSerializerModifier() {
@Override
public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc, JsonSerializer<?> serializer) {
if(beanDesc.getBeanClass().equals(Order.class)) {
return new UnwrappingOrderSerializer((BeanSerializerBase) serializer, NameTransformer.NOP);
}
return serializer;
}
});
}
});
}
public class UnwrappingOrderSerializer extends UnwrappingBeanSerializer {
public UnwrappingBarSerializer(BeanSerializerBase src, NameTransformer transformer) {
super(src, transformer);
}
@Override
public JsonSerializer<Object> unwrappingSerializer(NameTransformer transformer) {
return new UnwrappingOrderSerializer(this, transformer);
}
@Override
protected void serializeFields(Object bean, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonGenerationException {
Order order = (Order) bean;
jgen.writeStringField("paid", order.isPaid();
}
@Override
public boolean isUnwrappingSerializer() {
return true;
}
}