使用Jackson序列化时,如何让JSON中Long类型键保持数字而非字符串?
我之前也碰到过一模一样的情况!Jackson 2.x默认会把Map的非String类型键转成字符串输出,而且严格遵循JSON规范给键加上双引号。要实现你想要的数字形式键的输出,有两种方案可选,看你的具体需求来:
方案一:全局关闭字段名引号(生成非标准JSON)
如果你的整个JSON输出都不需要给键加双引号,并且能接受非标准的JSON格式(这种格式在JavaScript里能正常解析,但严格的JSON校验工具可能报错),可以直接配置ObjectMapper关闭QUOTE_FIELD_NAMES特性:
import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import java.util.HashMap; import java.util.Map; public class MapSerializationExample { public static void main(String[] args) throws Exception { Map<Long, String> processes = new HashMap<>(); processes.put(1L, "p1"); processes.put(2L, "p2"); ObjectMapper mapper = new ObjectMapper(); // 关闭字段名自动加双引号的特性 mapper.configure(JsonGenerator.Feature.QUOTE_FIELD_NAMES, false); // 可选:开启格式化输出,让JSON更易读 mapper.enable(SerializationFeature.INDENT_OUTPUT); String json = mapper.writeValueAsString(processes); System.out.println(json); } }
运行这段代码后,输出的结果就是你想要的格式:
{ 1 : "p1", 2 : "p2" }
⚠️ 注意:这种方式会让所有JSON对象的键都不带引号,如果你有其他String类型的键,也会变成无引号的形式,可能不符合预期。
方案二:自定义序列化器(仅处理Long类型键)
如果你只想让Long类型的键输出为数字形式,其他类型的键仍保持标准的带引号字符串,那可以自定义一个针对Long键的序列化逻辑:
步骤1:创建自定义的Long键序列化器
这个序列化器会专门处理Long类型的键,输出不带引号的数字形式:
import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import java.io.IOException; public class LongKeySerializer extends JsonSerializer<Long> { @Override public void serialize(Long key, JsonGenerator gen, SerializerProvider provider) throws IOException { // 直接写入数字形式的键,绕过默认的引号添加逻辑 gen.writeRaw(key.toString()); gen.writeRaw(": "); } }
步骤2:创建针对Long键Map的序列化器
为了完整控制Map的序列化过程,我们再写一个专门处理Map<Long, String>的序列化器:
import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import java.io.IOException; import java.util.Map; public class LongKeyMapSerializer extends JsonSerializer<Map<Long, String>> { @Override public void serialize(Map<Long, String> map, JsonGenerator gen, SerializerProvider serializers) throws IOException { gen.writeStartObject(); boolean firstEntry = true; for (Map.Entry<Long, String> entry : map.entrySet()) { if (!firstEntry) { gen.writeRaw(", "); } // 使用自定义Long键序列化器写入键 new LongKeySerializer().serialize(entry.getKey(), gen, serializers); // 写入带引号的值 gen.writeString(entry.getValue()); firstEntry = false; } gen.writeEndObject(); } }
步骤3:注册序列化器并使用
把自定义序列化器注册到ObjectMapper中,或者直接在Map字段上添加注解:
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; import java.util.HashMap; import java.util.Map; public class CustomSerializationExample { public static void main(String[] args) throws Exception { Map<Long, String> processes = new HashMap<>(); processes.put(1L, "p1"); processes.put(2L, "p2"); ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); // 注册自定义的Map序列化器 module.addSerializer(new LongKeyMapSerializer()); mapper.registerModule(module); // 可选:开启格式化输出 mapper.enable(SerializationFeature.INDENT_OUTPUT); String json = mapper.writeValueAsString(processes); System.out.println(json); } }
这样输出的结果同样是数字形式的Long键,而且只会影响Map<Long, String>类型的序列化,其他类型的键不受影响。
额外说明
严格来说,JSON规范要求对象的键必须是带双引号的字符串,你想要的{ 1: "p1", 2: "p2" }其实更接近JavaScript对象字面量,而非标准JSON。如果你的场景需要和严格遵循JSON规范的系统交互,建议还是保留字符串形式的键——反序列化的时候,Jackson会自动把字符串键转换为Long类型,完全不影响使用。
内容的提问来源于stack exchange,提问作者Sagar Pudi




