Jackson seamlessly replaces Fastjson

Table of contents

1. Fastjson to Jackson replacement plan

Scheme code

package main.java.solutions;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * json工具类
 * @Date: 2023/7/4 14:38
 */
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class JsonUtil {
    
    

    /**
     * 这个是提供给http接口使用的对象
     */
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    static {
    
    
        // 通过该方法对mapper对象进行设置,所有序列化的对象都将按改规则进行系列化
        // Include.Include.ALWAYS 默认
        // Include.NON_DEFAULT 属性为默认值不序列化
        // Include.NON_EMPTY 属性为 空("") 或者为 NULL 都不序列化,则返回的json是没有这个字段的。这样对移动端会更省流量
        // Include.NON_NULL 属性为NULL 不序列化
        OBJECT_MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        OBJECT_MAPPER.configure(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS, true);
        OBJECT_MAPPER.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        OBJECT_MAPPER.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
        OBJECT_MAPPER.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);

        SimpleModule module = new SimpleModule();
        module.addSerializer(BigDecimal.class, ToStringSerializer.instance);
        module.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer());
        module.addSerializer(LocalDate.class, new LocalDateSerializer());
        module.addDeserializer(LocalDateTime.class, new LocalDateTimeDeSerializer());
        module.addDeserializer(LocalDate.class, new LocalDateDeSerializer());
        OBJECT_MAPPER.registerModule(module);
    }

    /**
     * 直接获取ObjectMapper对象
     */
    public static ObjectMapper getObjectMapper() {
    
    
        return OBJECT_MAPPER;
    }

    /**
     * 序列化一个对象,序列化失败时仅仅打印日志并且返回null。
     *
     * @param object 对象
     * @return String
     */
    public static String toJsonOrNull(Object object) {
    
    
        if (object == null) {
    
    
            return null;
        }
        try {
    
    
            return OBJECT_MAPPER.writeValueAsString(object);
        } catch (JsonProcessingException e) {
    
    
            log.error("Json serialize error :", e);
        }
        return null;
    }

    /**
     * 序列化一个对象,序列化失败则抛异常
     *
     * @param object 对象
     * @return String
     */
    public static String toJsonString(Object object) throws Exception {
    
    
        if (object == null) {
    
    
            return null;
        }
        try {
    
    
            return OBJECT_MAPPER.writeValueAsString(object);
        } catch (JsonProcessingException e) {
    
    
            log.error("Json serialize error :", e);
            throw new Exception("Json serialize error");
        }
    }

    /**
     * 反序列化,失败则抛异常
     */
    public static <T> T parseObject(String json, Class<T> classType) throws Exception {
    
    
        if (StringUtils.isEmpty(json)) {
    
    
            return null;
        }
        try {
    
    
            return OBJECT_MAPPER.readValue(json, classType);
        } catch (Exception e) {
    
    
            log.error("Json de-serialize error :", e);
            throw new Exception("Json de-serialize error");
        }
    }

    /**
     * 自定义复杂的泛型对象,反序列化,失败则抛异常
     */
    public static <T> T parseObject(String json, TypeReference<T> typeReference) throws Exception{
    
    
        if (StringUtils.isEmpty(json)) {
    
    
            return null;
        }
        try {
    
    
            return OBJECT_MAPPER.readValue(json, typeReference);
        } catch (Exception e) {
    
    
            log.error("Json de-serialize error :", e);
            throw new Exception("Json de-serialize error");
        }

    }

    /**
     * 反序列化为Map
     */
    public static Map<String, Object> parseMap(String json) throws Exception{
    
    
        return parseObject(json, new TypeReference<Map<String, Object>>() {
    
    });
    }

    /**
     * 自定义 List 类型的反序列化
     *
     * @param json      JSON 字符串
     * @param classType List 中元素的类类型
     * @param <T>       List 中元素的泛型类型
     * @return 反序列化后的 List 对象
     */
    @SuppressWarnings("unchecked")
    public static <T> List<T> parseArray(String json, Class<T> classType) throws Exception {
    
    
        if (StringUtils.isEmpty(json)) {
    
    
            return null;
        }
        try {
    
    
            return parseCollection(json, ArrayList.class, classType);
        } catch (Exception e) {
    
    
            log.error("Error occurred during json deserialization for List: ", e);
            throw new Exception("Error occurred during json deserialization");
        }
    }

    /**
     * 通用的集合类型反序列化
     *
     * @param jsonStr     JSON 字符串
     * @param resultClass 结果集合的类类型
     * @param classType   集合元素的类类型
     * @param <C>         返回结果的泛型类型,必须是 Collection 的子类
     * @param <T>         集合元素的泛型类型
     * @return 反序列化后的集合对象
     * @throws IOException 如果反序列化过程中出现 I/O 错误
     */
    public static <C extends Collection<T>, T> C parseCollection(String jsonStr, Class<C> resultClass, Class<T> classType) throws IOException {
    
    
        return OBJECT_MAPPER.readValue(jsonStr, OBJECT_MAPPER.getTypeFactory().constructCollectionType(resultClass, classType));
    }

    public static Long getLong(Map<String, Object> map, String key) throws Exception {
    
    
        return TypeUtils.castToLong(map.get(key));
    }

    public static Integer getInteger(Map<String, Object> map, String key) throws Exception{
    
    
        return TypeUtils.castToInt(map.get(key));
    }

    public static String getString(Map<String, Object> map, String key) {
    
    
        return TypeUtils.castToString(map.get(key));
    }

}
package main.java.solutions;

import java.math.BigDecimal;
import java.util.Iterator;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 类型转换类
 * @Date: 2023/7/4 14:38
 */
public class TypeUtils {
    
    
    private static final Pattern NUMBER_WITH_TRAILING_ZEROS_PATTERN = Pattern.compile("\\.0*$");

    public static String castToString(Object value) {
    
    
        if (value == null) {
    
    
            return null;
        }
        return value.toString();
    }

    public static Integer castToInt(Object value) throws Exception{
    
    
        if (value == null) {
    
    
            return null;
        }
        if (value instanceof Integer) {
    
    
            return (Integer)value;
        }
        if (value instanceof BigDecimal) {
    
    
            return intValue((BigDecimal)value);
        }
        if (value instanceof Number) {
    
    
            return ((Number)value).intValue();
        }
        if (value instanceof String) {
    
    
            String strVal = (String)value;
            if (strVal.length() == 0 //
                || "null".equals(strVal) //
                || "NULL".equals(strVal)) {
    
    
                return null;
            }
            if (strVal.indexOf(',') != -1) {
    
    
                strVal = strVal.replaceAll(",", "");
            }
            Matcher matcher = NUMBER_WITH_TRAILING_ZEROS_PATTERN.matcher(strVal);
            if (matcher.find()) {
    
    
                strVal = matcher.replaceAll("");
            }
            return Integer.parseInt(strVal);
        }
        if (value instanceof Boolean) {
    
    
            return (Boolean)value ? 1 : 0;
        }
        if (value instanceof Map) {
    
    
            Map map = (Map)value;
            if (map.size() == 2
                && map.containsKey("andIncrement")
                && map.containsKey("andDecrement")) {
    
    
                Iterator iter = map.values().iterator();
                iter.next();
                Object value2 = iter.next();
                return castToInt(value2);
            }
        }
        throw new Exception("Cast type error: "+value);
    }

    public static Long castToLong(Object value) throws Exception {
    
    
        if (value == null) {
    
    
            return null;
        }
        if (value instanceof BigDecimal) {
    
    
            return longValue((BigDecimal)value);
        }
        if (value instanceof Number) {
    
    
            return ((Number)value).longValue();
        }
        if (value instanceof String) {
    
    
            String strVal = (String)value;
            if (strVal.length() == 0 //
                || "null".equals(strVal) //
                || "NULL".equals(strVal)) {
    
    
                return null;
            }
            if (strVal.indexOf(',') != -1) {
    
    
                strVal = strVal.replaceAll(",", "");
            }
            try {
    
    
                return Long.parseLong(strVal);
            } catch (NumberFormatException ex) {
    
    
                //
            }
        }
        if (value instanceof Map) {
    
    
            Map map = (Map)value;
            if (map.size() == 2
                && map.containsKey("andIncrement")
                && map.containsKey("andDecrement")) {
    
    
                Iterator iter = map.values().iterator();
                iter.next();
                Object value2 = iter.next();
                return castToLong(value2);
            }
        }
        if (value instanceof Boolean) {
    
    
            return (Boolean)value ? 1L : 0L;
        }
        throw new Exception("Cast type error: "+value);
    }

    public static int intValue(BigDecimal decimal) {
    
    
        if (decimal == null) {
    
    
            return 0;
        }
        int scale = decimal.scale();
        if (scale >= -100 && scale <= 100) {
    
    
            return decimal.intValue();
        }
        return decimal.intValueExact();
    }

    public static long longValue(BigDecimal decimal) {
    
    
        if (decimal == null) {
    
    
            return 0;
        }
        int scale = decimal.scale();
        if (scale >= -100 && scale <= 100) {
    
    
            return decimal.longValue();
        }
        return decimal.longValueExact();
    }
}

Serialization

1, JSON.toJSONString(this) and JSON.toJSON(this); If it is null, return null. If the conversion is abnormal, throw an exception.

If it is log printing, useJsonUtils.toJsonOrNull(this) instead, otherwise useJsonUtils. toJsonString(this)

Deserialization

1,JSON.parseObject(deviceInfoStr, DeviceInfo.class); if it is null, return null, if the conversion is abnormal, throw Exception

Replace with **JsonUtils.parseObject(deviceInfoStr, DeviceInfo.class)**

2,JSON.parseObject(String text);If it is null, return null, if the conversion is abnormal, throw an exception a>

Replace with **JsonUtils.parseMap(String json)**

3,JSONObject.parseArray(String text, Class<T> clazz); if is null, return null, if the conversion is abnormal , throw exception

Use **JsonUtils.parseArray(String text, Class<T> clazz)** to replace;

Get a value of a certain type through key

1,jsonObject.getLong(String key); if it is null, return null, if the conversion is abnormal, throw an exception a>

Use **JsonUtils.getLong(map, key)** to replace;

2,jsonObject.getInteger(String key); if it is null, return null, if the conversion is abnormal, throw an exception a>

Use **JsonUtils.getInteger(map, key)** to replace;

3,jsonObject.getString(String key); if it is null, return null, and if the conversion is abnormal, throw an exception a>

Use **JsonUtils.getString(map, key)** to replace;

type substitution

7, what is returned to the front end isJSONObjectUse **Map<String, Object>** to replace

8, what is returned to the front end isJSONArrayreplaced with **List<Object>**

2. Usage scenarios of serialization in Springboot project

  1. Controller: The controller is responsible for processing HTTP requests and responses, which involves the deserialization of request parameters and the serialization of response results. SpringMVC uses Message Converter (MessageConverter) to handle the serialization and deserialization of requests and responses. Common message converters include JSON, XML, Form Forms etc.
  2. RESTful API: If your Spring Boot project is based on the RESTful API architecture, then during the request and response process, serialization and deserialization between objects and JSON/XML data are required. Spring Boot uses message converters to handle these conversions automatically.
  3. Database operations: When using an Object Relational Mapping (ORM) framework (such as Hibernate, Spring Data JPA) for database operations, serialization and deserialization between Java objects and database records are required. The ORM framework automatically converts objects into database records (serialization), or database records into objects (deserialization).
  4. Cache operation: When using cache (such as Redis, Memcached), objects need to be stored in the cache or retrieved from the cache. This involves serializing and deserializing objects for storage and retrieval in the cache.
  5. Message queue: If a message queue (such as RabbitMQ, Kafka) is used in a Spring Boot project for asynchronous message processing, object serialization and deserialization need to be performed between the message producer and consumer so that during the message delivery process Correctly pass and handle object data.
  6. File Storage: When Java objects are stored as files, they may need to be serialized into JSON or other formats to make them easier to read and write.

3. Http message converter in SpringMVC framework

1. Principle:

Using the SpringMVC framework, we can use **@RequestBody and @ResponseBody in the code during development two annotations can complete the conversion from request message to object and from object to response message respectively. Within the source code, this flexible message conversion mechanism is actually implemented using HttpMessageConverter**.

The call of HttpMessageConverter is made in the method resolveArgument() of the RequestResponseBodyMethodProcessor class that parses the request parameters and the method handleReturnValue() that handles the return value.

Insert image description here

Http message converter interface

org.springframework.http.converter.HttpMessageConverter 3

public interface HttpMessageConverter<T> {
    
    
    boolean canRead(Class<?> var1, @Nullable MediaType var2);

    boolean canWrite(Class<?> var1, @Nullable MediaType var2);

    List<MediaType> getSupportedMediaTypes();

    T read(Class<? extends T> var1, HttpInputMessage var2) throws IOException, HttpMessageNotReadableException;

    void write(T var1, @Nullable MediaType var2, HttpOutputMessage var3) throws IOException, HttpMessageNotWritableException;
}

link

http请求 -DispatcherServlet -RequestMappingHandlerAdapter(处理请求映射的适配器)-ArgumentResolver(参数解析器)/ReturnValueHandlers(返回值处理器)-RequestResponseBodyMethodProcessor(处理请求和返回的参数)-HttpMessageConverter的read,write方法
    

org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#requestMappingHandlerAdapter 1

protected final List<HttpMessageConverter<?>> getMessageConverters() {
    
    
        if (this.messageConverters == null) {
    
    
            this.messageConverters = new ArrayList();
            //这是一个抽象方法,允许子类覆盖它来配置HTTP消息转换器。开发者可以在子类中实现这个方法来添加自定义的消息转换器。
            this.configureMessageConverters(this.messageConverters);
            if (this.messageConverters.isEmpty()) {
    
    
                //中添加一组默认的HTTP消息转换器。这些默认转换器是Spring MVC框架提供的,用于支持常见的数据格式,如JSON、XML等。
                this.addDefaultHttpMessageConverters(this.messageConverters);
            }
            //这是一个可选的方法,允许子类进一步扩展或修改已经配置的HTTP消息转换器。开发者可以在子类中实现这个方法来对已有的消息转换器进行额外的定制。
            this.extendMessageConverters(this.messageConverters);
        }

        return this.messageConverters;
    }

There are several types of converters:

Insert image description here

ByteArrayHttpMessageConverter: Responsible for reading data in binary format and writing data in binary format;

StringHttpMessageConverter: Responsible for reading data in string format and writing data in binary format (when the return value is or the accepted value is of String type, this is handled)

ResourceHttpMessageConverter: Responsible for readingresource files and writing resource file data;

ResourceRegionHttpMessageConverter: used to support chunked transfer (Chunked Transfer Encoding) response. It allows dividing resources into specified block sizes and transmitting responses to clients in blocks.

SourceHttpMessageConverter: used to process data represented in XML format or other text formats. It supports processing some Java source data (Source) types, such as javax.xml.transform.Source (source representation of XML data) and org.springframework.core.io.Resource (source representation of resource files).

AllEncompassingFormHttpMessageConverter: It is a comprehensive form data converter for processing form data requests and responses.

Jaxb2RootElementHttpMessageConverter: used to processXML data, and supports conversion between Java objects and XML data. It mainly relies on the Java Architecture for XML Binding (JAXB) API to implement serialization and deserialization of XML data.

MappingJackson2HttpMessageConverter: Responsible for reading and writing data in json format; (when the return value is an object or List, this is the process )

org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor 2

2. Custom message converter

Generally, the default converter cannot meet our needs. We need to customize the message converter. We can create our own converter class (Fastjson, Jackson message converter) and register it in the Spring Boot configuration. Typically, you just need to register your custom converter and Spring Boot will automatically apply it to the appropriate requests and responses.

Fastjson serialization message converter definition:

@Configuration
public class WebConfig implements WebMvcConfigurer {
    
    
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    
    
        //使用fastjson converter
        FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
        FastJsonConfig config = new FastJsonConfig();
        config.setSerializerFeatures(
            SerializerFeature.DisableCircularReferenceDetect,
            SerializerFeature.WriteNullListAsEmpty,
            SerializerFeature.WriteMapNullValue,
            SerializerFeature.WriteNullStringAsEmpty,
            SerializerFeature.WriteBigDecimalAsPlain);
        config.setSerializeFilters(ValueDesensitizeFilter.INSTANCE);

        // serialize config
        SerializeConfig serializeConfig = SerializeConfig.getGlobalInstance();
        serializeConfig.put(BigDecimal.class, ToStringSerializer.instance);
        serializeConfig.put(LocalDateTime.class, LocalDateTimeSerializer.instance);
        serializeConfig.put(Long.class, ToStringSerializer.instance);
        serializeConfig.put(Long.TYPE, ToStringSerializer.instance);
        config.setSerializeConfig(serializeConfig);
        converter.setFastJsonConfig(config);
        converters.add(0, converter);
    }
}

Jackson serialization message converter definition:

@Configuration
public class JacksonConfig {
    
    
    @Bean
    @ConditionalOnMissingBean(ObjectMapper.class)
    public ObjectMapper objectMapper() {
    
    
        ObjectMapper objectMapper = new ObjectMapper();
        // 通过该方法对mapper对象进行设置,所有序列化的对象都将按改规则进行序列化
        // Include.Include.ALWAYS 默认
        // Include.NON_DEFAULT 属性为默认值不序列化
        // Include.NON_EMPTY 属性为 空("") 或者为 NULL 都不序列化,则返回的json是没有这个字段的。这样对移动端会更省流量
        // Include.NON_NULL 属性为NULL 不序列化
        objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
        //解析数组集合、List对象 为[]
        objectMapper.setSerializerFactory(objectMapper.getSerializerFactory().withSerializerModifier(new SerializerModifier()));

        SimpleModule simpleModule = new SimpleModule();
        simpleModule.addSerializer(BigDecimal.class, ToStringSerializer.instance);
        simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
        simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
        simpleModule.addSerializer(LocalDateTime.class, new LocalDateTimeToTimestampSerializer());
        simpleModule.addSerializer(LocalDate.class, new LocalDateSerializer());
        simpleModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeSerializer());
        simpleModule.addDeserializer(LocalDate.class, new LocalDateDeSerializer());
        objectMapper.registerModule(simpleModule);

        return objectMapper;
    }
}

How to use Jackson elegantly for serialization and deserialization operations?

Define a general tool class. For special rules for attributes, you can add annotations to the attributes.

3. Jackson common annotations custom serialization rules

@JsonFormat annotation to customize date format.

public class User {
    
    
    private String name;
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date birthday; 
}

@JsonIgnore annotation to exclude fields that you do not want to serialize.

public class User {
    
    
    private String name;
    @JsonIgnore
    private String password;
}

@JsonProperty annotation to customize field names.

public class User {
    
    
    @JsonProperty("username")
    private String name;
}

@JsonInclude annotation to specify the null value handling strategy of the field.

public class User {
    
    
    private String name;
    @JsonInclude(JsonInclude.Include.NON_NULL)
    private String email;
}

Use the @JsonSerialize and @JsonDeserialize annotations to specify custom serializers and deserializers.

public class User {
    
    
    private String name;
    
    @JsonSerialize(using = LocalDateTimeSerializer.class)
    @JsonDeserialize(using = LocalDateTimeDeSerializer.class)
    private LocalDateTime birthday;
}

Custom deserializer:

/**
 * 新建这个反序列化器的目的:源String串中extendFieldJson属性是对象类型,目标对象DeviceInfo中的extendFieldJson属性是String类型,转换的时候会报类型不匹配的错误。
 *
 * @Date: 2023/9/19 18:00
 */
public class ObjectToStringDeSerializer extends JsonDeserializer<String> {
    
    

    @Override
    public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
    
    
        // 从 JSON 中读取属性的对象值
        Object objectValue = jsonParser.readValueAs(Object.class);
        // 将对象值转换为json字符串
        if (objectValue != null) {
    
    
            return JsonUtil.toJsonString(objectValue);
        }
        return null;
    }
}

Guess you like

Origin blog.csdn.net/Edward_hjh/article/details/134565363