Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/367.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何在Jackson JSON(反)序列化中使用自定义键类型自定义序列化或转换映射属性?_Java_Json_Jackson_Jackson Databind - Fatal编程技术网

Java 如何在Jackson JSON(反)序列化中使用自定义键类型自定义序列化或转换映射属性?

Java 如何在Jackson JSON(反)序列化中使用自定义键类型自定义序列化或转换映射属性?,java,json,jackson,jackson-databind,Java,Json,Jackson,Jackson Databind,我正在序列化的实例 @JsonIdentityInfo( generator = ObjectIdGenerators.PropertyGenerator.class, property = "id", scope=Entity1.class) public class Entity1 { private Long id; @JsonSerialize(converter = ValueMapListConverter.class) @JsonDe

我正在序列化的实例

@JsonIdentityInfo(
    generator = ObjectIdGenerators.PropertyGenerator.class,
    property = "id",
    scope=Entity1.class)
public class Entity1 {
    private Long id;
    @JsonSerialize(converter = ValueMapListConverter.class)
    @JsonDeserialize(converter = ValueMapMapConverter.class)
    private Map<Entity2, Integer> valueMap = new HashMap<>();

    public Entity1() {
    }

    public Entity1(Long id) {
        this.id = id;
    }

    [getter and setter]
}

添加了
@JsonSerialize
@JsonSerialize
,以便能够使用复杂键类型序列化映射。转换器是

public class ValueMapMapConverter extends StdConverter<List<Entry<Entity2, Integer>>, Map<Entity2, Integer>> {

    @Override
    public Map<Entity2, Integer> convert(List<Entry<Entity2, Integer>> value) {
        Map<Entity2, Integer> retValue = new HashMap<>();
        for(Entry<Entity2, Integer> entry : value) {
            retValue.put(entry.getKey(), entry.getValue());
        }
        return retValue;
    }
}
我研究了映射序列化,并确信我理解了基本概念,并且认为键序列化器是不必要的,因为转换首先发生,转换后的输出是一个不需要的列表

Entry
的序列化可能会有进一步的问题,我将通过使用不同的类(最终是我自己的类)来克服这些问题

可在以下网址找到SSCCE


我使用的是Jackson 2.9.4。

这里的问题是,当您使用键时,它必须是字符串,因为它会像
{“key”:value}
那样被序列化


您有两个选择


第一个选项如果可以将对象序列化为字符串,则可以将其用作json键

这在两种情况下都是可行的,当对象包含一个字段时(如您的示例中的字段)。e、 g

newsinglefieldobject(2l)//可以序列化为“2”
或者当包含多个可以表示为字符串的字段时。e、 g

new MultipleFieldObject(“John”,23)//可以序列化为“John 23岁”
现在自定义对象可以表示为字符串,您可以使用映射或条目列表

要使用简单映射,只需在注释中使用属性“keyUsing”,还必须定义自定义序列化程序和反序列化程序

public class MyKeyDeserializer extends KeyDeserializer {
    @Override
    public Entity2 deserializeKey(String key,
                                  DeserializationContext ctxt) throws IOException {
        return new Entity2(Long.parseLong(key));
    }
}

public class MyKeySerializer extends JsonSerializer<Entity2> {
    @Override
    public void serialize(Entity2 value,
                          JsonGenerator gen,
                          SerializerProvider serializers) throws IOException {
        gen.writeFieldName(value.getId().toString());
    }
}
这样的JSON被生成

{
    "id": 1,
    "valueMap": {
        "2": 21,
        "3": 22
    }
}
{
    "id": 1,
    "valueMap": [
        { "2": 21 },
        { "3": 22 }
    ]
}

要使用列表,您可以使用示例中的转换器,但要使用Entity2,您需要为键返回一个字符串

公共类ValueMapListConverter
扩展StdConverter{
@凌驾
公共列表转换(映射值){
列表结果=新建ArrayList();
for(条目:value.entrySet()){
添加(新的SimpleEntry(entry.getKey().getId().toString(),
entry.getValue());
}
返回结果;
}
}
公共类ValueMapConverter
扩展StdConverter{
@凌驾
公共地图转换(列表值){
Map retValue=newhashmap();
for(条目:值){
put(新的Entity2(Long.parseLong(entry.getKey())),entry.getValue());
}
返回值;
}
}
这样的JSON被生成

{
    "id": 1,
    "valueMap": {
        "2": 21,
        "3": 22
    }
}
{
    "id": 1,
    "valueMap": [
        { "2": 21 },
        { "3": 22 }
    ]
}
在这两种情况下,整数值都可能是一个复杂的对象


第二个选项是使用自定义对象,同样有多个选项,一个对象包含键的所有字段和值的字段

/。。。序列化-对象的反序列化
公共类CustomObject{
专用长id;/…所有密钥字段
私有int值;/…所有值字段
}
然后使用转换器
公共类ValueListMapConverter扩展了StdConverter
公共类ValueMapConverter扩展了StdConverter

这将生成如下的JSON

{
    "id": 1,
    "valueMap": [
        { "id": 2, "value": 21 },
        { "id": 3, "value": 22 }
    ]
}
{
    "id": 1,
    "valueMap": {
        "2": { "value": 21 },
        "3": { "value": 22 },
    }
}

您可以使用映射代替列表并使用键、键对象的其余字段以及自定义对象中的值字段

/。。。序列化-对象的反序列化
公共类CustomObject{
//…其余的关键字段
私有int值;/…所有值字段
}
转换器
公共类ValueListMapConverter扩展了StdConverter
公共类ValueMapConverter扩展了StdConverter

这将生成如下的JSON

{
    "id": 1,
    "valueMap": [
        { "id": 2, "value": 21 },
        { "id": 3, "value": 22 }
    ]
}
{
    "id": 1,
    "valueMap": {
        "2": { "value": 21 },
        "3": { "value": 22 },
    }
}

谢谢你详细的回答。我尝试了一些基于转换器的方法,但由于不同的原因失败了,考虑到afaiu Jackson 2.9在序列化支持方面存在大量问题,这似乎无关紧要,例如,这导致了与某些序列化实际上根本不受支持或不起作用的问题完全无关的失败。我在SSCCE的commit 2ea6797中创建了一个可工作的自定义序列化程序(这是第三个选项),尽管需要注意类型擦除问题,如中所述。我接受你的回答,因为它指出了关键部分“键必须是字符串”。我很高兴你合并了第三种方法并删除了第一种方法,因为它不适用于任意复杂的对象。您知道如何使映射序列化程序使用
@JsonIdentityInfo
提供的ID引用吗?好的,我可以删除第一种方法。但是你说第三个是什么意思?你的意思是扩展/改进。还有一些具体的测试现在失败了吗?我看到测试运行良好。参考
@JsonIdentityInfo
,至少我不知道序列化程序如何使用id(如果我理解正确,我们谈论的是
JsonIdentityInfo#property
)。当然,你可以使用反射并从注释中获得你想要的所有信息,但我不确定这是否是你想要的。我没有测试你的方法,因为我的第三个解决方案对我来说效果很好,但既然问题和答案对每个人都适用,拥有它是很好的(是的,如果你想要,我想扩展你的答案)。读取id引用的第二件事是使用
{f1:{……object1},f2:[1:{……object2}]}
而不是
{f1:{……object1},f2:[{……object1}:{……object2}]}
,其中
1
object1
的id,但这基本上是一个不同的问题,“继承”分支中的解决方案是第二种方法选项1。还有一个原因是,对于每个人来说,答案都适用于第一种方法,有人可能会使用它,所以我认为扩展第二种方法会很好。至于注释中的json,我不明白,您使用的是数组中的键,显然json键不能