how to resolve object references with a custom Jackson deserializer

Solution 1:

I guess jackson should be able to do it for you, but i couldn't figure out how. As a workaround you can write custom deserializer, in which you can cache the results by name property:

public class CachingColumnDeserializer extends JsonDeserializer<Column> {

  private static final Map<String, Column> MAP = new HashMap<>();

  @Override
  public Column deserialize(JsonParser parser, DeserializationContext context) throws IOException, JacksonException {
    JsonNode node = parser.getCodec().readTree(parser);
    String name = node.get("name").asText();
    return MAP.computeIfAbsent(name, nameKey -> new Column(nameKey, node.get("type").asText()));
  }

  public static Map<String, Column> getMap() {
    return Collections.unmodifiableMap(MAP);
  }
}

We need the static instance of the map in order to share it with KeyDeserializer, getMap() returns unmodifiableMap so we can't change it by mistake. Then your KeyDeserializer will use that map to get existing instances.

public class CachedColumnKeyDeserializer extends KeyDeserializer {

  private final Map<String, Column> map;

  public CachedColumnKeyDeserializer() {
    this.map = CachingColumnDeserializer.getMap();
  }

  @Override
  public Object deserializeKey(String key, DeserializationContext context) throws IOException {
    Column column = this.map.get(key);
    if (column == null) {
      return new Column(key, null);
    }
    return column;
  }
}

Specify how to deserialize Column class

@JsonDeserialize(using = CachingColumnDeserializer.class, keyUsing = CachedColumnKeyDeserializer.class)

Just to be on the safe side you can specify you need to deserialize columns before other properties

@JsonPropertyOrder({"name", "columns", ...})