JSon - 嵌套映射的自定义键序列化

4
我有一个嵌套的Map<StructureNode, Map<String, String>>,需要自定义键序列化器和反序列化器(StructureNode包含对其他对象的引用,这些对象需要作为此映射的键)。我使用了以下方法:

Jackson Modules for Map Serialization

得到以下结果。 自定义序列化器:
public class StructureNodeKeySerializer extends JsonSerializer<StructureNode> {

    private static final ObjectMapper mapper = new ObjectMapper();

    @Override
    public void serialize(StructureNode value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        StringWriter writer = new StringWriter();
        mapper.writeValue(writer, value.copyUpwards());
        gen.writeFieldName(writer.toString());
    }
}

自定义反序列化器:

public class StructureNodeKeyDeserializer extends KeyDeserializer  {

    private static final ObjectMapper mapper = new ObjectMapper();

    @Override
    public Object deserializeKey(String key, DeserializationContext ctxt) throws IOException {
        return mapper.readValue(key, StructureNode.class);
    }
}

使用方法:

@JsonDeserialize(keyUsing = StructureNodeKeyDeserializer.class) @JsonSerialize(keyUsing = StructureNodeKeySerializer.class)
private Map<StructureNode, String> structureIds;
@JsonDeserialize(keyUsing = StructureNodeKeyDeserializer.class) @JsonSerialize(keyUsing = StructureNodeKeySerializer.class)
private Map<StructureNode, Map<String, String>> metadata;

这段代码可以正确地序列化一个 Map<StructureNode, String>,但如果应用于一个嵌套的 Map<StructureNode, Map<String, String>>,则会出现以下错误:

Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: java.lang.String cannot be cast to structure.StructureNode

Jackson似乎正在使用相同的自定义序列化方法来处理"子映射"。是否有一种好的方法可以解决这个问题,而不是用另一个自定义(非Map)对象替换"子映射"?


你不应该在你的序列化和反序列化中使用私有的 ObjectMapper - teppic
@teppic 你为什么这么说?我是在遵循我提到的另一篇帖子中的示例,但很乐意改进它。但无论如何,这并不能解决我遇到的异常问题。 - tb189
因为它与客户端代码使用的代码不同(并且可能已被检测仪器修改)。 - teppic
2个回答

3
您可以使用以下方法解决这个问题:
public static class Bean{
    @JsonSerialize(using = MapStructureNodeKeySerializer.class)
    public Map<StructureNode, Map<String, String>> metadata;
}

并以不同的方式实现您的序列化程序:

public static class MapStructureNodeKeySerializer 
        extends JsonSerializer<Map<StructureNode, Object>> {

    private static final ObjectMapper mapper = new ObjectMapper();

    @Override
    public void serialize(Map<StructureNode, Object> value, JsonGenerator gen, 
                          SerializerProvider serializers) throws IOException {
        gen.writeStartObject();

        for(Map.Entry<StructureNode, Object> val: value.entrySet()){
            // your custom serialization code here
            StringWriter writer = new StringWriter();
            mapper.writeValue(writer, val.getKey().copyUpwards());

            gen.writeObjectField(writer.toString(), val.getValue());
        }

        gen.writeEndObject();
    }
}

如果你想保留 keyUsing = StructureNodeKeySerializer.class
public static class Bean{
    @JsonSerialize(keyUsing = StructureNodeKeySerializer.class)
    public Map<StructureNode, Map<String, String>> metadata;
}

您可以这样实现:

您可以像这样实现:

public static class StructureNodeKeySerializer extends JsonSerializer {

    private static final ObjectMapper mapper = new ObjectMapper();

    @Override
    public void serialize(Object value, JsonGenerator gen,
                          SerializerProvider serializers) throws IOException {

        if (value instanceof StructureNode){ // <= type of 1-st level Map key
            // your custom serialization code here
            StringWriter writer = new StringWriter();
            mapper.writeValue(writer, ((StructureNode)value).copyUpwards());
            gen.writeFieldName(writer.toString());
        }else if(value instanceof String){   // <= type of 2-nd level Map key
            gen.writeFieldName((String) value);
        }
    }
}

0

如果你想更通用地将它序列化为keySerializer,你可以将 else 子句改写如下:

if (value instanceof StructureNode) {
  // ...
} else {
  serializers
    .findKeySerializer(value.class, null)
    .serialize(value, gen, serializers);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接