使用Jackson反序列化时参考现有对象

13

JSON

{
  "schools": [
    {
      "id": 1,
      "name": "School A"
    },
    {
      "id": 2,
      "name": "School B"
    }
  ],
  "students": [
    {
      "id": 1,
      "name": "Bobby",
      "school": 1
    }
  ]

}

如何将JSON映射到以下类中,使得Bobby的学校被映射到已实例化的School A中。

public class School {
  private Integer id;
  private String name;
}

public class Student {
  private Integer id;
  private String name;
  private School school;
}

我尝试过一些有点奇怪的东西来操作 Student 类...

public class Student {
  private Integer id;
  private String name;
  private School school;

  @JsonProperty("school")
  public void setSchool(Integer sid) {
    for (School school : getSchools()) {
      if (school.id == sid) {
        this.school = school;
        break;
      }
    }
  }
}

我的问题是JSON数据中学校和学生同时被解析,所以我不确定如何得到学校列表。也许我应该分别解析它们,这样我就可以先得到学校列表了?


你尝试过什么吗?不清楚你卡在哪里了。 - 4castle
抱歉,我尝试用一个我一直在尝试采取的方法的示例来更好地解释我正在努力解决的问题。 - Rawr
2个回答

6
杰克逊将为您完成。只需使用@JsonIdentityInfo注释您的对象即可:
@JsonIdentityInfo(scope=School.class, generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
public class School {
    private Integer id;
    private String name;

    public School() {
    }

    public School(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

@JsonIdentityInfo(scope=Student.class, generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
public class Student {
    private Integer id;
    private String name;
    private School school;

    public Student() {
    }

    public Student(Integer id, String name, School school) {
        this.id = id;
        this.name = name;
        this.school = school;
    }

    public Integer getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public School getSchool() {
        return school;
    }

    public void setSchool(School school) {
        this.school = school;
    }
}

public static void main(String[] args) throws IOException {
    School school = new School(1, "St Magdalene's");
    Student mary = new Student(1, "Mary", school);
    Student bob = new Student(2, "Bob", school);
    Student[] students = new Student[] {mary, bob};

    // Write out
    String serialized = mapper.writeValueAsString(students);
    System.out.println("Serialized: " + serialized);
    // Read in
    Student[] deserialized = mapper.readValue(serialized, Student[].class);
}

2
没成功。序列化后的结果如下: [{"id":1,"name":"Mary","school":{"id":1,"name":"St Magdalene's"}},{"id":2,"name":"Bob","school":1}] 反序列化后,它没有链接到学校对象,而是只存储了学校的ID。 - Rawr
抱歉,我应该检查一下结果。没有类型信息,反序列化会变成哈希集合列表。我已经更新了示例,使用了一个Student[],这样Jackson就有足够的信息来构建对象了。 - teppic
作为替代方案,您可以让Jackson管理ids。删除“id”属性并使用“generator=ObjectIdGenerators.IntSequenceGenerator”。 - teppic
当我使用默认属性@id时,我遇到了反序列化问题(JsonMappingException: Unexpected end-of-input: was expecting closing),因此我不得不使用另一个属性。请注意,在@JsonIdentityInfo中定义的属性名称不需要存在于Java对象中(您可以定义任何名称,例如myJacksonId)。最终,我只在School类上使用了@JsonIdentityInfo(scope=School.class, generator= ObjectIdGenerators.IntSequenceGenerator.class, property = "myJacksonId")(在Student类上不需要)。 - Julien Kronegg

3

以下是定义的类:

@JsonIdentityInfo(property = "id", generator = ObjectIdGenerators.PropertyGenerator.class)
class School {
    public Integer id;
    public String name;
}

class Student {
    public Integer id;
    public String name;
    @JsonIdentityReference(alwaysAsId = true)
    public School school;
}

class All {
    public List<School> schools;
    public List<Student> students;
}

这完全按照您的意图工作:

@Test
public void test() throws JsonProcessingException {
    var json = "{" +
            "\"schools\":[" +
            "{\"id\":1,\"name\":\"School A\"}," +
            "{\"id\":2,\"name\":\"School B\"}" +
            "]," +
            "\"students\":[" +
            "{\"id\":1,\"name\":\"Bobby\",\"school\":1}" +
            "]" +
            "}";
    var mapper = new ObjectMapper();
    var all =  mapper.readValue(json, All.class);
    Assertions.assertThat(all.students.get(0).school).isSameAs(all.schools.get(0));
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接