Java 如何为映射使用Jackson自定义值序列化程序<;字符串,对象>;?

Java 如何为映射使用Jackson自定义值序列化程序<;字符串,对象>;?,java,jackson,Java,Jackson,我有一个来自DB的结果集,其形式为Map,我应该从REST服务返回json。映射中的值可以是各种类型,包括PGObject、String、Integer和Date 我还为类型为“jsonb”的org.postgresql.util.PGObject类编写了一个自定义序列化程序,它在列表中运行良好,但在映射中则不行 当Jackson在Map中序列化这个PGObject值时,我会得到json值,如下所示: "reason": { "type": "jsonb", "value":

我有一个来自DB的结果集,其形式为
Map
,我应该从REST服务返回
json
。映射中的值可以是各种类型,包括
PGObject
String
Integer
Date

我还为类型为“jsonb”的
org.postgresql.util.PGObject
类编写了一个自定义序列化程序,它在
列表
中运行良好,但在
映射
中则不行

当Jackson在
Map
中序列化这个
PGObject
值时,我会得到
json
值,如下所示:

  "reason": {
    "type": "jsonb",
    "value": "[{\"id\": 6, \"name\": \"Foo\"}, {\"id\": 7, \"name\": \"Bar\"}, {\"id\": 8, \"name\": \"Baz\"}]"
  }
我需要的是:

  "reason": [
    {
      "id": 6,
      "name": "Foo"
    },
    {
      "id": 7,
      "name": "Bar"
    },
    {
      "id": 8,
      "name": "Baz"
    },
  ],
我已尝试将模块添加到
ObjectMapper
中,并将自定义
MapType
添加到
ObjectWriter
中,如下所示:


当在
映射的序列化过程中发现PGObject作为值时,如何在Jackson中启用我的
PGObjectSerializer

一个解决方法是为
对象添加序列化程序

然后在序列化程序本身中,您可以检查对象是否是
PGobject
的实例

在myMapType中,您可以指定
对象.class

JavaType myMapType = mapper.getTypeFactory().
                constructMapType(HashMap.class, String.class, Object.class);
序列化程序将是:

class PgObjectSerializer extends JsonSerializer<Object> {
@Override
public void serialize(Object object, JsonGenerator gen, SerializerProvider serializers) throws IOException {
    if (object instanceof PGobject) {
        PGobject pgObject = (PGobject) object;
        switch (pgObject.getType()) {
        case "json":
        case "jsonb":
                gen.writeRawValue(pgObject.getType());
                break;
        default:
                gen.writeString(pgObject.getType());
        }
        }else{
                ObjectMapper mapper = new ObjectMapper();
                mapper.writeValue(gen, object);
        }
    }
}
类PgObjectSerializer扩展JsonSerializer{ @凌驾 public void serialize(对象对象、JsonGenerator gen、SerializerProvider序列化程序)引发IOException{ if(PGobject的对象实例){ PGobject PGobject=(PGobject)对象; 开关(pgObject.getType()){ 案例“json”: 案例“jsonb”: gen.writeRawValue(pgObject.getType()); 打破 违约: gen.writeString(pgObject.getType()); } }否则{ ObjectMapper mapper=新的ObjectMapper(); mapper.writeValue(gen,object); } } }
此解决方法需要将所有其他类类型的序列化委托给其他“基”或“泛型”序列化程序。我没有找到这样一个。您可以使用obejct映射器来实现这一点,请参阅我更新的PGObjectSerializeRun。幸运的是,
else
部分导致字符串键序列化期间出现异常:
由以下原因引起:com.fasterxml.jackson.core.jsongGenerationException:无法写入字符串,需要字段名(上下文:Object)
。我尝试了
serializers.defaultSerializeValue(value,gen)
gen.writeString(value.toString())
但得到了相同的错误。尝试
mapper.writeValue(gen,object)模块
进行了一个小的修复之后,我也在answer中更新了code>。谢谢尝试使用module.addSerializer()而不是addKeySerializer。我认为密钥序列化程序只适用于密钥。@oleg.cherednik,感谢您指出这一点,这就是原因!我已经改为
addSerializer
,现在我的代码运行得非常好。
@Service
@Transactional
public class MyClass {
    private final ObjectWriter writer;
    private final MyRepo repository;

    @Autowired
    public MyClass(MyRepo repository) {
        this.repository = repository;

        SimpleModule module = new SimpleModule();
        module.addKeySerializer(PGobject.class, new PgObjectSerializer());

        ObjectMapper mapper = new ObjectMapper();
        JavaType myMapType = mapper.getTypeFactory().
                constructMapType(HashMap.class, String.class, PGobject.class);
        writer = mapper.registerModule(module).writerFor(myMapType);
    }

    ...

    private String toJsonString(Map<String, Object> map) {
        try {
            return writer.writeValueAsString(map);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }
}
[Test worker] ERROR 
com.fasterxml.jackson.databind.JsonMappingException: object is not an instance of declaring class (through reference chain: java.util.HashMap["end_date"]->java.lang.String["type"])
    at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:388)
    at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:348)
    at com.fasterxml.jackson.databind.ser.std.StdSerializer.wrapAndThrow(StdSerializer.java:343)
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:698)
    at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeFieldsUsing(MapSerializer.java:736)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:534)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:30)
    at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:416)
    at com.fasterxml.jackson.databind.ObjectWriter$Prefetch.serialize(ObjectWriter.java:1425)
    at com.fasterxml.jackson.databind.ObjectWriter._configAndWriteValue(ObjectWriter.java:1158)
    at com.fasterxml.jackson.databind.ObjectWriter.writeValueAsString(ObjectWriter.java:1031)
JavaType myMapType = mapper.getTypeFactory().
                constructMapType(HashMap.class, String.class, Object.class);
class PgObjectSerializer extends JsonSerializer<Object> {
@Override
public void serialize(Object object, JsonGenerator gen, SerializerProvider serializers) throws IOException {
    if (object instanceof PGobject) {
        PGobject pgObject = (PGobject) object;
        switch (pgObject.getType()) {
        case "json":
        case "jsonb":
                gen.writeRawValue(pgObject.getType());
                break;
        default:
                gen.writeString(pgObject.getType());
        }
        }else{
                ObjectMapper mapper = new ObjectMapper();
                mapper.writeValue(gen, object);
        }
    }
}