I have two classes like this:
public class A {
String aProp = "aProp";
public String getAProp() {
return aProp;
}
}
public class B {
String bProp = "bProp";
A a = new A();
@JsonProperty("bProp")
public String getBProp() {
return bProp;
}
@JsonSerialize(using = CustomSerializer.class)
public A getA() {
return a;
}
}
I'm expecting to get JSON like this:
{
"bProp": "bProp", // just serizlised bProp
"sProp1": "sProp1_aProp", // computed using aProp
"sProp2": "sProp2_aProp" // computed another way
}
So I wrote custom JsonSerializer
like this:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
public class CustomSerializer extends JsonSerializer<A> {
@Override
public void serialize(A a, JsonGenerator json, SerializerProvider provider) throws IOException {
json.writeStringField("sProp1", "sProp1_" + a.getAProp());
json.writeStringField("sProp2", "sProp2_" + a.getAProp());
}
}
But I keep getting an error:
com.fasterxml.jackson.core.JsonGenerationException: Can not write a field name, expecting a value
Unless I put json.writeStartObject();
and json.writeEndObject();
in serialize method (so it produces wrong JSON).
So I'm looking for a solution like @JsonUnwrapped
to use with custom JsonSerializer
.
I understand your problem and the thing that you need is UnwrappingBeanSerializer
. You can see another related SO post:
Different JSON output when using custom json serializer in Spring Data Rest
The problem is that you cannot have both annotations @JacksonUnwrapped
and @JsonSerialize
in one field because when you have @JsonSerializer
Jackson will always write field name.
Here is the complete solution:
public class CustomSerializer extends UnwrappingBeanSerializer {
public CustomSerializer(BeanSerializerBase src, NameTransformer transformer) {
super(src, transformer);
}
@Override
public JsonSerializer<Object> unwrappingSerializer(NameTransformer transformer) {
return new CustomSerializer(this, transformer);
}
@Override
protected void serializeFields(Object bean, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonGenerationException {
A a = (A) bean;
jgen.writeStringField("custom", a.getAProp());
jgen.writeStringField("custom3", a.getAProp());
}
@Override
public boolean isUnwrappingSerializer() {
return true;
}
}
Test case, you should redefine your object mapper with custom configuration or research for other method .
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@SpringApplicationConfiguration(classes = Application.class)
public class ColorsTest {
ObjectMapper mapper = new ObjectMapper();
@Before
public void setUp(){
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(A.class)) {
return new CustomSerializer((BeanSerializerBase) serializer, NameTransformer.NOP);
}
return serializer;
}
});
}
});
}
@Test
public void testSerializer() throws JsonProcessingException {
System.out.println(mapper.writeValueAsString(new B()));
}
}
Class B:
public class B {
@JsonProperty("bProp")
public String getBProp() {
return "bProp";
}
@JsonUnwrapped
public A getA() {
return new A();
}
}
I like to add this post and solution to the question asked here: Using custom Serializers with JsonUnwrapperd as the original poster is using JsonSerializer
as I am. The suggest approach with the UnwrappingBeanSerializer
won't work in this case. My post has a slightly different goal, but the idea from the post should be applicable to your use case easily, as it is just overwriting one more method and not having to add bunch of stuff apart from JsonUnwrapped
on the property.
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import java.io.IOException;
public class Test {
static class A {
String aProp = "aProp";
public String getAProp() {
return aProp;
}
}
static class B {
String bProp = "bProp";
A a = new A();
@JsonProperty("bProp")
public String getBProp() {
return bProp;
}
@JsonSerialize(using = CustomSerializer.class)
@JsonUnwrapped
public A getA() {
return a;
}
}
static class CustomSerializer extends JsonSerializer<A> {
@Override
public boolean isUnwrappingSerializer() {
return true;
}
@Override
public void serialize(A a, JsonGenerator json, SerializerProvider provider) throws IOException {
json.writeStringField("sProp1", "sProp1_" + a.getAProp());
json.writeStringField("sProp2", "sProp2_" + a.getAProp());
}
}
public static void main(String... a) throws Exception {
final ObjectMapper o = new ObjectMapper();
o.enable(SerializationFeature.INDENT_OUTPUT);
System.out.println(o.writeValueAsString(new B()));
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With