【问题标题】:Serialize and Deserialize Map<Object, Object> Jackson序列化和反序列化 Map<Object, Object> Jackson
【发布时间】:2019-11-26 16:59:45
【问题描述】:

我有以下类,我想通过 Jackson (2.9.10) 使用其 ID 引用对象进行 JSON 序列化/反序列化。

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Key {

    @JsonProperty("id")
    private String id;
    @JsonProperty("random_field_key")
    private boolean randomFieldKey;

    // Getters and setters
}
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Value {

    @JsonProperty("id")
    private String id;
    @JsonProperty("random_field_value")
    private boolean randomFieldValue;

    // Getters and setters
}
@JsonPropertyOrder({"keys", "values", "relationships"})
public class Relationship {
    @JsonProperty("keys")
    private List<Key> keys;
    @JsonProperty("values")
    private List<Value> values;
    @JsonProperty("relationships")
    private Map<Key, Value> relationships;

    // Getters and setters
}
public class Main {
    private static final ObjectMapper mapper = new ObjectMapper();

    public static void main(String[] args) throws IOException {


        Value value = new Value();
        value.setId("valueId");
        value.setRandomFieldValue(true);

        Key key = new Key();
        key.setId("keyId");
        key.setRandomFieldKey(false);

        Map<Key, Value> map = new HashMap<Key, Value>();
        map.put(key, value);

        Relationship relationship = new Relationship();
        relationship.setKeys(Collections.singletonList(key));
        relationship.setValues(Collections.singletonList(value));
        relationship.setRelationships(map);

        String serialisedRelationship = mapper.writeValueAsString(relationship);
        Relationship deserialisedRelationship = mapper.readValue(serialisedRelationship, Relationship.class);

    }
}

预期结果如下:

{
    "keys": [
        {
            "id": "keyId",
            "random_field_key": false
        }
    ],
    "values": [
        {
            "id": "valueId",
            "random_field_value": true
        }
    ],
    "relationships": {
        "keyId": "valueId"
    }
}

相反,我得到:

{
    "keys": [
        {
            "id": "keyId",
            "random_field_key": false
        }
    ],
    "values": [
        {
            "id": "valueId",
            "random_field_value": true
        }
    ],
    "relationships": {
        "util.teststack.Key@8646db9": "valueId"
    }
}

我添加了一个自定义序列化器,它允许将 Key 序列化为 Id(我认为 JsonIdentityInfo 注释会解决这个问题)并设法获得预期的结果。

public class KeySerializer extends JsonSerializer<Key> {

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

        gen.writeFieldName(value.getId());
    }
}
@JsonPropertyOrder({"keys", "values", "relationships"})
public class Relationship {
    @JsonProperty("keys")
    private List<Key> keys;
    @JsonProperty("values")
    private List<Value> values;
    @JsonSerialize(keyUsing = KeySerializer.class)
    @JsonProperty("relationships")
    private Map<Key, Value> relationships;

    // Getters and setters
}

我似乎无法反序列化 JSON,但我总是遇到异常:

Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot find a (Map) Key deserializer for type [simple type, class util.teststack.Key]

虽然添加自定义反序列化器也是一个好主意,但我很难理解如何引用“外部”(已经实例化)的 Key 实例。我在 DeserializerContext 中似乎能做的就是实例化一个新的密钥,而不是引用之前创建的密钥。

检查 DeserializerContext 我可以看到我需要的所有实例都在那里,但我不明白如何引用(并返回它们)。 令人惊讶的是,映射值的引用在没有序列化器/反序列化器的情况下也能正常工作。

public class KeyDeserializer extends com.fasterxml.jackson.databind.KeyDeserializer {

    @Override
    public Key deserializeKey(
            String key,
            DeserializationContext ctxt) {

        Key newKey = new Key();
        newKey.setId(key);

        return newKey;
    }
}
@JsonPropertyOrder({"keys", "values", "relationships"})
public class Relationship {
    @JsonProperty("keys")
    private List<Key> keys;
    @JsonProperty("values")
    private List<Value> values;
    @JsonSerialize(keyUsing = KeySerializer.class)
    @JsonDeserialize(keyUsing = KeyDeserializer.class)
    @JsonProperty("relationships")
    private Map<Key, Value> relationships;
}

Debugger

我实现了接受的答案,以这种方式解决问题:

public class KeyDeSerializer extends com.fasterxml.jackson.databind.KeyDeserializer {

    @Override
    public Key deserializeKey(String key, DeserializationContext ctxt) {
        Relationship obj = (Relationship) ctxt.getParser().getParsingContext().getParent().getCurrentValue();
        return getKey(key, obj);
    }

    private Key getKey(String key, Relationship obj) {
        for (Key ck : obj.getKeys()) {
            if (ck.getId().equals(key)) {
                return ck;
            }
        }
        return null;
    }
}

【问题讨论】:

  • 我很好奇:自从接受了答案,你最后是怎么解决的?
  • 我使用公认的解决方案来导航 JSON 并检索与密钥关联的正确对象并在 KeyDeserializer 中返回它。我应该在哪里发布带有工作解决方案的 MWE?

标签: java json serialization jackson


【解决方案1】:

在对象 DeserializationContext 中,您可以检索您的 Relationship 对象。

试试这样的

ctxt.getParser().getParsingContext().getParent().getCurrentValue();

从这个对象中你可以检索到你需要的实例

【讨论】:

  • 这个单行解决方案看起来简洁且格式正确。也许您还可以解释最初出了什么问题(缺少密钥解串器)以及这一行如何有助于正确序列化密钥。这将使您的回答成为任何面临类似情况的人的一般收据
【解决方案2】:

您能否尝试在您的 Key 类中覆盖 toString() 方法并返回“id”的值。

public String toString() {
  this.id;
}

【讨论】:

  • 谢谢。多亏了这一点,我可以避免设置序列化程序(而不是获得“util.teststack.Key@8646db9”或类似的值,我得到了实际的 id),但它仍然无法反序列化类。
  • 你不应该为你的反序列化类扩展 JsonDeserializer 吗?类似于序列化?
  • 你也可以删除这个“@JsonSerialize(keyUsing = KeySerializer.class) @JsonDeserialize(keyUsing = KeyDeserializer.class)”
  • 我试过删除这两个注释,但结果是一样的。我正在按照我在网上找到的所有示例(来自 baeldung 和类似网站)和官方文档来扩展 KeyDeserializer。这可以避免反序列化整个节点并专门关注 Map 键。如果我要使用标准反序列化器,我将不得不返回整个属性“关系”。我试过了,但还是不明白如何从反序列化上下文中引用之前实例化的对象。
猜你喜欢
  • 2014-05-19
  • 1970-01-01
  • 1970-01-01
  • 2018-08-24
  • 1970-01-01
  • 1970-01-01
  • 2017-05-09
  • 2023-04-07
  • 2021-09-07
相关资源
最近更新 更多