Java 防止Jackson ObjectMapper自动转换字符串()-格式化复合映射键

Java 防止Jackson ObjectMapper自动转换字符串()-格式化复合映射键,java,json,jackson,tostring,Java,Json,Jackson,Tostring,我正在使用com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString()从JavaPOJO生成JSON 我的一个对象(我们称之为ResultSheet)包含一个属性映射结果ResultId表示一个复合主键(由两个FK组成) 在遇到ResultId时,ObjectMapper似乎意识到这不能直接用JSON表示,因为JSON映射键必须是字符串。因此,它将输出ResultId的toString()。(在这种特殊情况下,这会生成一个格式为

我正在使用
com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString()
从JavaPOJO生成JSON

我的一个对象(我们称之为
ResultSheet
)包含一个属性
映射结果
ResultId
表示一个复合主键(由两个FK组成)

在遇到
ResultId
时,
ObjectMapper
似乎意识到这不能直接用JSON表示,因为JSON映射键必须是字符串。因此,它将输出
ResultId
toString()
。(在这种特殊情况下,这会生成一个格式为
“42:43”
的字符串,但这只是因为
toString()
当前正是这样实现的。)

我怎样才能阻止它这样做?当前行为隐藏了这样一个事实,即被封送的结构不能正确地表示为JSON结构。它还将当前的
toString()
实现泄漏到我的web服务的API中

我宁愿抛出一个异常,告诉我何时用作映射键的值不是
字符串
——或者至少,何时转换为
字符串
。(例如,我不太担心
Long
键是
toString()
-ed。)

我已经研究了
序列化功能
设置,但它们似乎都没有解决这个问题

有没有办法防止这种情况?是否可以以一般方式进行,即不只是针对该特定类,而是在将来可能出现这种情况的任何情况下,添加新类(其中一些可能用作映射键)时


注意,在这种情况下,使
toString()
抛出异常将不是一种可接受的黑客行为。

要解决此问题,您可以实现并注册自己的
com.fasterxml.jackson.databind.ser.BeanSerializerModifier
类。和重写方法。实现总是可以返回引发异常的序列化器。请参见以下示例:

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import lombok.AllArgsConstructor;
import lombok.Data;

import java.io.IOException;
import java.util.Collections;
import java.util.Map;

public class JsonKeySerializerApp {
    public static void main(String[] args) throws IOException {
        SimpleModule keyExceptionSerializerModule = new SimpleModule();
        keyExceptionSerializerModule.setSerializerModifier(new BeanSerializerModifier() {
            @Override
            public JsonSerializer<?> modifyKeySerializer(SerializationConfig config, JavaType valueType, BeanDescription beanDesc, JsonSerializer<?> serializer) {
                // specify classes you want to allow to be serialized as keys
                if (valueType.getRawClass().getPackage().getName().startsWith("java.")) {
                    return serializer;
                }
                return new JsonSerializer<Object>() {
                    @Override
                    public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) {
                        throw new UnsupportedOperationException("You can not serialize POJO as keys!");
                    }
                };
            }
        });
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(keyExceptionSerializerModule);

        Map<ResultId, Long> map = Collections.singletonMap(new ResultId(1, 2), 1L);

        mapper.writeValue(System.out, map);
    }
}

@AllArgsConstructor
@Data
class ResultId {

    private int id1;
    private int id2;
}

所以您希望
映射
不序列化为
{“”:,…}
,而是序列化为
[{“键”:,“值”:},…]
?与将
Map
转换为带有
Map.Entry
作为名称/值对的JSON对象不同,您基本上希望将映射转换为
列表
?否。如果键不是字符串,我希望它完全失败。我不希望它只是假设它可以
toString()
映射中的(非字符串)键,然后继续执行,这看起来很有希望。谢谢
{Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: You can not serialize POJO as keys! (through reference chain: java.util.Collections$SingletonMap["ResultId(id1=1, id2=2)"])
    at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:394)
    at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:353)
    at com.fasterxml.jackson.databind.ser.std.StdSerializer.wrapAndThrow(StdSerializer.java:316)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeFields(MapSerializer.java:725)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:643)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:33)