Exploring Data Types and Default Values in RESTful APIs with Spring Boot API design
In designing REST APIs, handling data types and default values carefully is essential to creating user-friendly and robust interfaces. As developers, we can construct APIs that are intuitive, adaptable, and aligned with user expectations by understanding these concepts and their implications.
Data types, the fundamental building blocks of programming, wield significant influence when shaping API contracts. The purpose of the API implementation is to enable developers of web applications who wish to build APIs to manipulate the data as desired.
Assume we have an application that have some API and can make request and response.
Now I create a class and extended from DefaultSerializerProvider name CustomDefaultSerializerProvider also @Override
some functions createInstance, copy and findNullValueSerializer
public class CustomDefaultSerializerProvider extends DefaultSerializerProvider {
public CustomDefaultSerializerProvider() {
super();
}
public CustomDefaultSerializerProvider(CustomDefaultSerializerProvider src) {
super(src);
}
protected CustomDefaultSerializerProvider(CustomDefaultSerializerProvider provider, SerializationConfig config, SerializerFactory jsf) {
super(provider, config, jsf);
}
@Override
public CustomDefaultSerializerProvider createInstance(SerializationConfig config, SerializerFactory jsf) {
return new CustomDefaultSerializerProvider(this, config, jsf);
}
@Override
public DefaultSerializerProvider copy() {
if (getClass() != CustomDefaultSerializerProvider.class)
return super.copy();
return new CustomDefaultSerializerProvider(this);
}
@Override
public JsonSerializer<Object> findNullValueSerializer(BeanProperty property) throws JsonMappingException {
if (String.class.isAssignableFrom(property.getType().getRawClass()))
return new DefaultJsonSerializer(Strings.EMPTY);
else if (this.isClassDecimal(property.getType().getRawClass()))
return new DefaultJsonSerializer(BigDecimal.valueOf(BigDecimal.ZERO.doubleValue()));
else if (Number.class.isAssignableFrom(property.getType().getRawClass()))
return new DefaultJsonSerializer(BigInteger.ZERO);
else if (Boolean.class.isAssignableFrom(property.getType().getRawClass()))
return new DefaultJsonSerializer(Boolean.FALSE);
else if (this.isClassDate(property.getType().getRawClass()))
return new DefaultJsonSerializer(null);
else if (Collection.class.isAssignableFrom(property.getType().getRawClass()))
return new DefaultJsonSerializer(new ArrayList<>());
else if (Object.class.isAssignableFrom(property.getType().getRawClass()))
return new DefaultJsonSerializer(null);
else
return super.findNullValueSerializer(property);
}
private boolean isClassDate(Class<?> clazz) {
return Date.class.isAssignableFrom(clazz) || LocalDate.class.isAssignableFrom(clazz) || LocalDateTime.class.isAssignableFrom(clazz);
}
private boolean isClassDecimal(Class<?> clazz) {
return Float.class.isAssignableFrom(clazz) || BigDecimal.class.isAssignableFrom(clazz) || Double.class.isAssignableFrom(clazz);
}
private static class DefaultJsonSerializer extends JsonSerializer<Object> {
private final Object defaultValue;
public DefaultJsonSerializer(Object defaultValue) {
this.defaultValue = defaultValue;
}
@Override
public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeObject(this.defaultValue);
}
}
}
Depending on yours, for me I made default value for:
String is empty string (“”)
Number is zero (0)
Boolean is FALSE(false)
Collection is the empty array ([])
and other data type keep same for null
The last just set or register it to ObjectMapper
@Configuration
public class AppConfig {
@Bean
ObjectMapper mapper(Jackson2ObjectMapperBuilder mapperBuilder) {
ObjectMapper mapper = mapperBuilder.build();
SerializerProvider serializerProvider = mapper.getSerializerProvider();
if (ObjectUtils.isNotEmpty(serializerProvider))
mapper.setSerializerProvider(new CustomDefaultSerializerProvider());
mapper.setPropertyNamingStrategy(new PropertyNamingStrategies.SnakeCaseStrategy());
return mapper;
}
}
The Result from:
{
"status": "200",
"result": "Succeeded",
"data": {
"id": "5",
"name": null,
"gender": null,
"username": "jonh",
"status": "ACTIVE",
"is_graduated": null,
"skills": null,
"age": null
}
}
To
{
"status": "200",
"result": "Succeeded",
"data": {
"id": "5",
"name": "",
"gender": "",
"username": "jonh",
"status": "ACTIVE",
"is_graduated": false,
"skills": [],
"age": 0
}
}
Thank you for reading until the end. Before you go:
- Please consider clapping and following the writer! 👏
- Visit phsophea101.github.io to find out more about how we are democratizing free programming education around the world.
- If you like my work and would like to buy me a coffee please go this buymeacoffee link. Thank you :)