![JAR search and dependency download from the Maven repository](/logo.png)
com.infilos.spring.config.JsonConfigure Maven / Gradle / Ivy
package com.infilos.spring.config;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.node.ValueNode;
import com.infilos.relax.Json;
import com.infilos.relax.json.JsonException;
import com.infilos.spring.utils.CodeBasedEnum;
import com.infilos.spring.utils.NameBasedEnum;
import com.infilos.utils.Datetimes;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import java.io.IOException;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.util.concurrent.ConcurrentHashMap;
@Configuration
@AutoConfigureAfter(WebMvcAutoConfiguration.class)
public class JsonConfigure {
private static final ConcurrentHashMap, JsonDeserializer>> ENUM_DESER_CACHE = new ConcurrentHashMap<>();
@Bean
@Primary
public ObjectMapper objectMapper() {
Json.underMapper().disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
Json.underMapper().configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
Json.underMapper().setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
Json.underMapper().registerModule(customTimeModule());
Json.underMapper().registerModule(customEnumModule());
return Json.underMapper();
}
@Bean
public MappingJackson2HttpMessageConverter jackson2HttpMessageConverter() {
return new MappingJackson2HttpMessageConverter(objectMapper());
}
/**
* Serialize and deserialize date/datetime with standard format.
*/
private static SimpleModule customTimeModule() {
SimpleModule module = new SimpleModule();
module.addSerializer(LocalDate.class, new LocalDateSerializer());
module.addSerializer(LocalDateTime.class, new LocalDatetimeSerializer());
module.addSerializer(ZonedDateTime.class, new ZonedDatetimeSerializer());
module.addSerializer(Timestamp.class, new TimestampSerializer());
module.addDeserializer(LocalDate.class, new LocalDateDeserializer());
module.addDeserializer(LocalDateTime.class, new LocalDatetimeDeserializer());
module.addDeserializer(ZonedDateTime.class, new ZonedDatetimeDeserializer());
module.addDeserializer(Timestamp.class, new TimestampDeserializer());
return module;
}
private static final class LocalDateSerializer extends JsonSerializer {
@Override
public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider provider)
throws IOException {
gen.writeString(value.format(Datetimes.Patterns.AtDays));
}
}
private static final class LocalDatetimeSerializer extends JsonSerializer {
@Override
public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider provider)
throws IOException {
gen.writeString(value.format(Datetimes.Patterns.AtSeconds));
}
}
private static final class ZonedDatetimeSerializer extends JsonSerializer {
@Override
public void serialize(ZonedDateTime value, JsonGenerator gen, SerializerProvider provider)
throws IOException {
gen.writeString(value.format(Datetimes.Patterns.AtSeconds));
}
}
private static final class TimestampSerializer extends JsonSerializer {
@Override
public void serialize(Timestamp value, JsonGenerator gen, SerializerProvider serializerProvider)
throws IOException {
gen.writeString(Datetimes.from(value.getTime()).format(Datetimes.Patterns.AtSeconds));
}
}
private static final class LocalDateDeserializer extends JsonDeserializer {
@Override
public LocalDate deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException {
JsonNode node = p.getCodec().readTree(p);
if (node.isTextual() && StringUtils.isNotBlank(node.textValue())) {
return LocalDate.parse(node.textValue(), Datetimes.Patterns.AtDays);
}
throw JsonException.of("Deserialize LocalDate failed: " + node.toString());
}
}
private static final class LocalDatetimeDeserializer extends JsonDeserializer {
@Override
public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException {
JsonNode node = p.getCodec().readTree(p);
if (node.isTextual() && StringUtils.isNotBlank(node.textValue())) {
return LocalDateTime.parse(node.textValue(), Datetimes.Patterns.AtSeconds);
}
throw JsonException.of("Deserialize LocalDateTime failed: " + node.toString());
}
}
private static final class ZonedDatetimeDeserializer extends JsonDeserializer {
@Override
public ZonedDateTime deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException {
JsonNode node = p.getCodec().readTree(p);
if (node.isTextual() && StringUtils.isNotBlank(node.textValue())) {
return ZonedDateTime.parse(node.textValue(), Datetimes.Patterns.AtSeconds);
}
throw JsonException.of("Deserialize ZonedDateTime failed: " + node.toString());
}
}
private static final class TimestampDeserializer extends JsonDeserializer {
@Override
public Timestamp deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException {
JsonNode node = p.getCodec().readTree(p);
if (node.isTextual() && StringUtils.isNotBlank(node.textValue())) {
return Timestamp.valueOf(
LocalDateTime.parse(node.textValue(), Datetimes.Patterns.AtSeconds));
}
throw JsonException.of("Deserialize Timestamp failed: " + node.toString());
}
}
private static Module customEnumModule() {
SimpleModule module = new SimpleModule();
module.setDeserializerModifier(new BeanDeserializerModifier() {
@Override
@SuppressWarnings("unchecked")
public JsonDeserializer> modifyEnumDeserializer(DeserializationConfig config,
JavaType type,
BeanDescription beanDescr,
JsonDeserializer> deserializer) {
if (beanDescr.getBeanClass().isEnum()) {
if (CodeBasedEnum.class.isAssignableFrom(beanDescr.getBeanClass())) {
ENUM_DESER_CACHE.computeIfAbsent(
beanDescr.getBeanClass(),
k -> new CodeBasedEnumDeserializer<>((Class>) beanDescr.getBeanClass())
);
return ENUM_DESER_CACHE.get(beanDescr.getBeanClass());
// return new CodeBasedEnumDeserializer<>((Class>) beanDescr.getBeanClass());
} else if (NameBasedEnum.class.isAssignableFrom(beanDescr.getBeanClass())) {
ENUM_DESER_CACHE.computeIfAbsent(
beanDescr.getBeanClass(),
k -> new NameBasedEnumDeserializer<>((Class>) beanDescr.getBeanClass())
);
return ENUM_DESER_CACHE.get(beanDescr.getBeanClass());
// return new NameBasedEnumDeserializer<>((Class>) beanDescr.getBeanClass());
}
}
return deserializer;
}
});
return module;
}
private static class CodeBasedEnumDeserializer, E extends Enum> extends JsonDeserializer {
private final Class> enumClass;
public CodeBasedEnumDeserializer(Class> enumClass) {
this.enumClass = enumClass;
}
@Override
@SuppressWarnings("unchecked")
public T deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
TreeNode node = jsonParser.getCodec().readTree(jsonParser);
Integer code = null;
try {
if (node.isValueNode()) {
ValueNode valueNode = (ValueNode) node;
code = Integer.parseInt(valueNode.asText());
return CodeBasedEnum.fromCode((Class) enumClass, code);
}
} catch (Exception e) {
throw new IllegalArgumentException(String.format("%s cannot match the code: %s", enumClass.getSimpleName(), code));
}
return null;
}
}
private static class NameBasedEnumDeserializer, E extends Enum> extends JsonDeserializer {
private final Class> enumClass;
public NameBasedEnumDeserializer(Class> enumClass) {
this.enumClass = enumClass;
}
@Override
@SuppressWarnings("unchecked")
public T deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
TreeNode node = jsonParser.getCodec().readTree(jsonParser);
String name = null;
try {
if (node.isValueNode()) {
ValueNode valueNode = (ValueNode) node;
name = valueNode.asText();
return NameBasedEnum.fromName((Class) enumClass, name);
}
} catch (Exception e) {
throw new IllegalArgumentException(String.format("%s cannot match the name: %s", enumClass.getSimpleName(), name));
}
return null;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy