Reputation: 141
The problem I am having is that I have a common object which is used in many places, in this example the common object is Student. Student is a property of another object called Enrolment (however it could be a property of many many other classes). Now without custom serialization being applied to Student I will get something like this:
{"session":"9am Wednesday","student":{"firstName":"Joe","lastName":"Bloggs"}}
What I would like to do is to apply a custom serializer to Student so that anywhere it appears in my code, in this example for instance it is inside the Enrolment class, I will get this:
{"session":"9am Wednesday","firstName":"Joe","lastName":"Bloggs"}
Or if I so choose this:
{"session":"9am Wednesday","first":"Joe","last":"Bloggs"}
Or maybe even this:
{"session":"9am Wednesday","name":"Joe Bloggs"}
Here is my example code:
public class Enrolment {
private String session;
private Student student;
public Enrolment(String session, Student student) {
this.session = session;
this.student = student;
}
public String getSession() {
return session;
}
public void setSession(String session) {
this.session = session;
}
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
}
@JsonSerialize(using = StudentSerializer.class)
public class Student {
private String firstName;
private String lastName;
public Student(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
public class StudentSerializer extends JsonSerializer<Student> {
@Override
public void serialize(Student value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
// ??????
}
}
public class Launcher {
public static void main(String[] args) throws Exception{
ObjectMapper mapper = new ObjectMapper();
Student student = new Student("Joe", "Bloggs");
Enrolment enrolment = new Enrolment("9am Wednesday", student);
System.out.println(mapper.writeValueAsString(enrolment));
}
}
It would be a massive bonus if you could provide a deserializer too so that it could accept one of the desired json serialized examples and create the Student object from it. Again the deserializer just like the serializer has to be attached to the Student object so that anywhere it appears it will perform the same action.
Thank you :)
Upvotes: 0
Views: 1345
Reputation: 141
Here is another option I have stumbled upon:
public class Enrolment {
private String session;
private Student student;
public Enrolment(String session, Student student) {
this.session = session;
this.student = student;
}
public String getSession() {
return session;
}
public void setSession(String session) {
this.session = session;
}
@JsonUnwrapped
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
}
public class StudentUnwrappingBeanSerializer extends UnwrappingBeanSerializer {
public StudentUnwrappingBeanSerializer(BeanSerializerBase src, NameTransformer transformer) {
super(src, transformer);
}
@Override
public JsonSerializer<Object> unwrappingSerializer(NameTransformer transformer) {
return new StudentUnwrappingBeanSerializer(this, transformer);
}
@Override
protected void serializeFields(Object bean, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonGenerationException {
Student student = (Student) bean;
jgen.writeStringField("first", student.getFirstName());
jgen.writeStringField("last", student.getLastName());
}
@Override
public boolean isUnwrappingSerializer() {
return true;
}
}
public class Launcher {
public static void main(String[] args) throws Exception{
ObjectMapper mapper = new 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(Student.class)) {
return new StudentUnwrappingBeanSerializer((BeanSerializerBase) serializer, NameTransformer.NOP);
}
return serializer;
}
});
}
});
Student student = new Student("Joe", "Bloggs");
ExtendableOption<StudyType> studyType = new ExtendableOption<>(StudyType.DISTANCE);
Enrolment enrolment = new Enrolment("9am Wednesday", student, studyType);
System.out.println(mapper.writeValueAsString(enrolment));
}
}
Upvotes: 0
Reputation: 3121
You want the @JsonUnwrapped
annotation available from version 1.9 onwards. You can check the documentation for version 2.0 here, but basically you would have:
public class Enrolment {
...
@JsonUnwrapped
public Student getStudent() {
return student;
}
...
}
And you would get your first option:
{"session":"9am Wednesday","firstName":"Joe","lastName":"Bloggs"}
The annotation relies on the default serializer. On the one hand, you cannot have a custom serializer for Student
but, on the other hand, you can use other annotations like @JsonProperty
and customize Student
to have your second option.
Your third option would be done best by also adding a custom getter and setter in Student
. In this case you would use @JsonIgnore
to avoid serializing the other properties.
Upvotes: 1