wangguangwu
wangguangwu
发布于 2024-08-24 / 23 阅读
0
0

解决 JSON 字符串字段二次序列化问题:基于 FastJSON2 和 Jackson 的自定义序列化器实现

1. 问题是什么

在 Java 开发中,使用 FastJSON2 或其他序列化库进行对象序列化时,可能会遇到一个常见的问题:当对象的某个字段已经是 JSON 格式的字符串时,序列化库会再次对其进行序列化,导致嵌套的 JSON 格式。此时,我们希望这个字段能够原样输出,而不是再次被包裹或转义。

2. 问题的导致原因

这种问题的根源在于序列化库默认的处理方式。当序列化库遇到一个字符串字段时,它会将其视为普通字符串,并进行转义或包裹处理,即使该字符串本身已经是一个合法的 JSON 格式。这个问题在处理复杂的数据结构时,尤其是涉及嵌套的 JSON 数据时,可能会引发数据解析错误或增加解析难度。

3. 问题的解决思路

为了解决这个问题,我们可以自定义一个序列化器来处理这类特殊字段。自定义序列化器会检测字段内容是否为 JSON 格式的字符串。如果是 JSON 格式,则直接将其原样写入输出,而不进行二次序列化;如果不是 JSON 格式,则按正常字符串处理。这样可以避免不必要的嵌套或转义,保持字段的原始格式。

4. 如何自定义序列化器

4.1 FastJSON2 的自定义序列化器

import com.alibaba.fastjson2.JSONWriter;
import com.alibaba.fastjson2.writer.ObjectWriter;
import org.springframework.stereotype.Component;

import java.lang.reflect.Type;

@Component
public class RawJsonSerializer implements ObjectWriter<String> {

    @Override
    public void write(JSONWriter writer, String value, Object fieldName, Type fieldType, long features) {
        if (isJson(value)) {
            writer.writeRaw(value); // 直接写入原始 JSON 字符串,避免二次序列化
        } else {
            writer.writeString(value); // 正常序列化为字符串
        }
    }

    private boolean isJson(String str) {
        return str != null && (str.trim().startsWith("{") || str.trim().startsWith("["));
    }
}

4.2 Jackson 的自定义序列化器

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Component
public class RawJsonSerializer extends JsonSerializer<String> {

    @Override
    public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        if (isJson(value)) {
            gen.writeRawValue(value); // 直接写入原始 JSON 字符串,避免二次序列化
        } else {
            gen.writeString(value); // 正常序列化为字符串
        }
    }

    private boolean isJson(String str) {
        return str != null && (str.trim().startsWith("{") || str.trim().startsWith("["));
    }
}

5. 如何注册自定义序列化器到 Spring 上下文

5.1 FastJSON2 注册自定义序列化器

import com.alibaba.fastjson2.JSON;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FastJsonConfig {

    private final RawJsonSerializer rawJsonSerializer;

    public FastJsonConfig(RawJsonSerializer rawJsonSerializer) {
        this.rawJsonSerializer = rawJsonSerializer;
    }

    @Bean
    public void configureFastJson() {
        JSON.register(String.class, rawJsonSerializer); // 注册自定义序列化器
    }
}

5.2 Jackson 注册自定义序列化器

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class JacksonConfig {

    private final RawJsonSerializer rawJsonSerializer;

    public JacksonConfig(RawJsonSerializer rawJsonSerializer) {
        this.rawJsonSerializer = rawJsonSerializer;
    }

    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        SimpleModule module = new SimpleModule();
        module.addSerializer(String.class, rawJsonSerializer); // 注册自定义序列化器
        objectMapper.registerModule(module);
        return objectMapper;
    }
}

6. 测试用例

6.1 FastJSON2 测试用例

import com.alibaba.fastjson2.JSON;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

    @GetMapping("/test")
    public String test() {
        String jsonString = "{\"key\":\"value\"}";

        // 使用FastJSON2进行序列化,使用自定义序列化器
        return JSON.toJSONString(jsonString);
    }
}

6.2 Jackson 测试用例

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

    @Autowired
    private ObjectMapper objectMapper;

    @GetMapping("/test")
    public String test() throws Exception {
        String jsonString = "{\"key\":\"value\"}";

        // 使用Jackson进行序列化,使用自定义序列化器
        return objectMapper.writeValueAsString(jsonString);
    }
}

7. 测试结果

访问 /test 端点后,返回的 JSON 字符串不会再被包裹或转义。具体输出如下:

{ "key": "value" }

而不是:

"\"{\"key\":\"value\"}\""

8. 总结

自定义序列化器在处理复杂 JSON 结构时非常有用,尤其是在需要避免二次序列化的情况下。通过注册自定义序列化器,我们可以确保序列化库如 FastJSON2 或 Jackson 按预期处理字段内容。本文提供了在 Spring 上下文中如何注册和使用自定义序列化器的详细步骤,帮助开发者解决实际开发中的常见问题。


评论