简体   繁体   中英

Serialize and Deserialize Map<Object, Object> Jackson

I have the following classes that I would like to JSON serialise/deserialise with Jackson (2.9.10) referencing the object using its ID.

@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);

    }
}

The expected result is the following:

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

Instead, I get:

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

I added a custom Serializer that allows serializing Key as an Id (I thought the JsonIdentityInfo annotation would take care of that) and managed to get the expected result.

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
}

I can't seem though to deserialise the JSON and I always get the exception:

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

I though it would've been a good idea to add a custom Deserializer as well, but I'm struggling understanding how to reference an "external" (already instantiated) Key instance. All I can seem to do inside the DeserializerContext is instantiate a new Key, instead of referencing the previously created one.

Inspecting the DeserializerContext I can see that all the instances I need are there, but I don't understand how to reference (and return them). Surprisingly the reference for the Map Value is working correctly without a serializer/deserializer.

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

I implemented the accepted answer managing to solve the problem this way:

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;
    }
}

Could you please try overriding toString() method in your Key class and return the value of "id".

public String toString() {
  this.id;
}

Inside the object DeserializationContext you are able to retrive your Relationship object.

Try something like this

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

From this object you can retrieve the instance you need

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM