Probably the easiest way is to implement custom serialiser for User.Status
and produce different output for different kinds of representation.
class UserStatusJsonSerializer extends JsonSerializer<User.Status> {
@Override
public void serialize(User.Status value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
if (gen instanceof ToXmlGenerator) {
ToXmlGenerator toXmlGenerator = (ToXmlGenerator) gen;
serializeXml(value, toXmlGenerator);
} else {
gen.writeString(value.getMaritalStatus());
}
}
private void serializeXml(User.Status value, ToXmlGenerator toXmlGenerator) throws IOException {
toXmlGenerator.writeStartObject();
toXmlGenerator.setNextIsAttribute(true);
toXmlGenerator.writeFieldName("type");
toXmlGenerator.writeString(value.getType());
toXmlGenerator.setNextIsAttribute(false);
toXmlGenerator.writeRaw(value.getMaritalStatus());
toXmlGenerator.writeEndObject();
}
@Override
public boolean isEmpty(SerializerProvider provider, User.Status value) {
return value == null || value.getMaritalStatus() == null;
}
}
Since now, you can remove extra XML
annotations and register custom serialiser:
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
@Data
class User implements Serializable {
private static final long serialVersionUID = 1L;
private Status status;
private String name;
@Builder
@Value
@JsonSerialize(using = UserStatusJsonSerializer.class)
public static class Status {
private String maritalStatus;
private String type = "text";
}
}
Simple console app usage could look like below:
import com.fasterxml.jackson.annotation.JsonInclude;
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 com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.Value;
import java.io.IOException;
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
public class JsonPathApp {
public static void main(String[] args) throws Exception {
List<User> users = Arrays.asList(
createUser("John", "married"),
createUser("Tom", null));
ObjectMapper jsonMapper = JsonMapper.builder()
.enable(SerializationFeature.INDENT_OUTPUT)
.serializationInclusion(JsonInclude.Include.NON_EMPTY)
.build();
for (User user : users) {
System.out.println(jsonMapper.writeValueAsString(user));
System.out.println();
}
XmlMapper xmlMapper = XmlMapper.builder()
.enable(SerializationFeature.INDENT_OUTPUT)
.serializationInclusion(JsonInclude.Include.NON_EMPTY)
.build();
for (User user : users) {
System.out.println(xmlMapper.writeValueAsString(user));
System.out.println();
}
}
private static User createUser(String name, String maritalStatus) {
return User.builder()
.name(name)
.status(User.Status.builder()
.maritalStatus(maritalStatus)
.build())
.build();
}
}
Above code prints
JSON for John:
{
"status" : "married",
"name" : "John"
}
JSON for Tom:
{
"name" : "Tom"
}
XML for John:
<User>
<status type="text">married</status>
<name>John</name>
</User>
XML for Tom
<User>
<name>Tom</name>
</User>
Notice, that we implemented UserStatusJsonSerializer#isEmpty
method which defines what empty
means for a Status
class. Now, we need to enable JsonInclude.Include.NON_EMPTY
feature in your Spring Boot
application. Add below key to your application configuration file:
spring.jackson.default-property-inclusion=non_empty
If you do not want to enable inclusion globally you can enable it only for one property using @JsonInclude
annotation.
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private Status status;
See also: