You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Jackson的版本依赖反序列化:未定义引用异常抛出问题

接口多实现类反序列化时的未定义引用异常问题

我需要根据JSON数据的version字段,将其反序列化为同一接口DatabaseI的不同实现类(Database1或Database2),目前通过自定义反序列化器已实现该功能。但我的数据结构中使用了对象引用(通过Jackson的@JsonIdentityInfo和@JsonIdentityReference注解实现),预期遇到未定义引用时抛出异常,但当前结合接口的实现方式无法触发该异常。

补充信息:若在readValue中直接使用具体类而非接口(例如用mapper.readValue(buggy, Database2.class);替代mapper.readValue(buggy, DatabaseI.class);),预期的异常抛出行为正常,但会失去对JSON具体内容的抽象能力。

以下是包含未通过测试用例的示例代码:

import static org.junit.jupiter.api.Assertions.assertThrows;

import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.node.IntNode;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.JsonIdentityReference;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.Test;


public class Example {

  @Test
  public void test() throws JsonProcessingException {

    ObjectMapper mapper =
        new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
            .configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);

    SimpleModule module = new SimpleModule();
    module.addDeserializer(DatabaseI.class, new ToyDeserializer());
    mapper.registerModule(module);

    String correct = "{'version':1,'people':[{'id':'a','friends':['b','c']},{'id':'b','friends':['c']},{'id':'c','friends':['b']}]}";
    DatabaseI deserCorrect = mapper.readValue(correct, DatabaseI.class);
    System.out.println(mapper.writeValueAsString(deserCorrect));

    String buggy = "{'version':2,'people':[{'id':'a','friends':['b','c']},{'id':'b','friends':['c']},{'id':'c','friends':['FOO']}]}";
    assertThrows(Exception.class, () -> {
      mapper.readValue(buggy, DatabaseI.class);
    }, "The reference FOO is undefined. An Exception should be thrown.");
  }
}

class Person {

  @JsonProperty("id")
  private String id;

  @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,
      property = "id")
  @JsonIdentityReference(alwaysAsId = true)
  private List<Person> friends = new ArrayList<>();


  public Person() {
  }

  public String getId() {
    return id;
  }

  public void setId(String id) {
    this.id = id;
  }

  public List<Person> getFriends() {
    return friends;
  }

  public void setFriends(List<Person> friends) {
    this.friends = friends;
  }
  
}

interface DatabaseI {

}

class Database1 implements DatabaseI {

  private int version;
  private List<Person> people = new ArrayList<>();

  public Database1() {
  }

  public List<Person> getPeople() {
    return people;
  }

  public void setPeople(List<Person> people) {
    this.people = people;
  }

  public int getVersion() {
    return version;
  }

  public void setVersion(int version) {
    this.version = version;
  }
}

class Database2 implements DatabaseI {

  private String version;
  private List<Person> people = new ArrayList<>();

  public Database2() {
  }

  public List<Person> getPeople() {
    return people;
  }

  public void setPeople(List<Person> people) {
    this.people = people;
  }

  public String getVersion() {
    return version;
  }

  public void setVersion(String version) {
    this.version = version;
  }
}

class ToyDeserializer extends StdDeserializer<DatabaseI> {

  protected ToyDeserializer(Class<?> vc) {
    super(vc);
  }

  public ToyDeserializer() {
    this(null);
  }

  @Override
  public DatabaseI deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JacksonException {
    ObjectMapper mapper = (ObjectMapper) jp.getCodec();
    JsonNode node = mapper.readTree(jp);

    int version = (Integer) ((IntNode) node.get("version")).numberValue();
    if (version == 1) {
      return mapper.treeToValue(node, Database1.class);
    } else {
      return mapper.treeToValue(node, Database2.class);
    }
  }
}

内容的提问来源于stack exchange,提问作者heinrich

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 15:50:25