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 上下文中如何注册和使用自定义序列化器的详细步骤,帮助开发者解决实际开发中的常见问题。