在某些情况下,在正文(例如 JSON 正文)中序列化或反序列化的数据包含对同一对象的引用。例如,一个包含球员列表的 JSON 正文,以及由这些球员组成的球队列表:
{
"players": [
{ "name": "Player 1" },
{ "name": "Player 2" },
{ "name": "Player 3" },
{ "name": "Player 4" },
{ "name": "Player 5" },
{ "name": "Player 6" },
{ "name": "Player 7" },
{ "name": "Player 8" }
],
"teams": [
{
"name": "Team 1",
"players": [
{ "name": "Player 1"},
{ "name": "Player 2"}
]
},
{
"name": "Team 2",
"players": [
{ "name": "Player 3"},
{ "name": "Player 4"}
]
},
{
"name": "Team 3",
"players": [
{ "name": "Player 5"},
{ "name": "Player 6"}
]
},
{
"name": "Team 4",
"players": [
{ "name": "Player 7"},
{ "name": "Player 8"}
]
}
]
}
正如您想象的那样,玩家 X 指的是同一个对象,但我们最终可能会遇到一个不需要的场景,即 玩家 X 由不同的对象表示。
我想知道这些情况下最好和最常用的方法是什么。我可以想到几种方法:
- 将 ID 属性添加到
Player
类。我的设计不包含 ID,因为不需要它。识别一个对象的方法是通过它的引用,如果它们包含在一个集合中,则通过它们在其中的位置(如果集合有位置)。这可能被认为是一种不好的做法,但我是故意这样做的,除非必要,否则我不打算更改它。 - 还有一个球员 ID,但不是将球队序列化/反序列化为球员列表,它只是一个 ID 列表。 JSON 正文中包含的信息会丢失,但我们会得到更紧凑的数据。
- 坚持我的无 ID 设计,与前一点类似,我可以有一个位置列表(实际上这将是一个玩家 ID,因为它们用作识别手段),所以数字 0 指的是 players 列表中第一个位置的玩家。
- 更改契约(Contract)并让球员拥有独特的名字。有了这个,我们现在可以拥有一个 ID,而无需向类添加新属性。但是我认为这是一个坏主意,因为不一定所有玩家都应该有不同的名字。
什么是最好的方法?通常做什么?你有什么不同的建议吗?
最佳答案
我达到了接近问题要求的程度: 我用了 jackson 的 Object Identity Feature它允许定义某个属性的值来标识 POJO 的不同实例:
@JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class,
property="name", scope=Player.class)
public class Player
{
public String name;
public Player() {}
public Player(String name) { this.name = name; }
public String toString() {
// prints name and java object id
return name + "-" + super.toString();
}
}
这是使用的其余 POJO:
public class League
{
public List<Player> players;
public List<Team> teams;
}
public class Team
{
public String name;
public List<Player> players;
public Team() {}
public Team(String name) { this.name = name; }
public String toString() {
return name + "-" + super.toString() + ":" + players.toString();
}
}
测试方法:
public static void main(String[] args)
{
ObjectMapper objectMapper = new ObjectMapper();
try (Reader reader = new FileReader("C:/Temp/xx.json")) {
League l = objectMapper.readValue(reader, League.class);
System.out.println("l.players");
System.out.println(l.players);
System.out.println("l.teams");
System.out.println(l.teams);
} catch (Exception e) {
e.printStackTrace();
}
}
输出清楚地表明玩家和团队使用了相同的对象:
l.players
[Player 1-test.JSONTest$Player@641147d0, Player 2-test.JSONTest$Player@6e38921c, Player 3-test.JSONTest$Player@64d7f7e0, Player 4-test.JSONTest$Player@27c6e487, Player 5-test.JSONTest$Player@49070868, Player 6-test.JSONTest$Player@6385cb26, Player 7-test.JSONTest$Player@38364841, Player 8-test.JSONTest$Player@28c4711c]
l.teams
[Team 1-test.JSONTest$Team@59717824:[Player 1-test.JSONTest$Player@641147d0, Player 2-test.JSONTest$Player@6e38921c], Team 2-test.JSONTest$Team@146044d7:[Player 3-test.JSONTest$Player@64d7f7e0, Player 4-test.JSONTest$Player@27c6e487], Team 3-test.JSONTest$Team@1e9e725a:[Player 5-test.JSONTest$Player@49070868, Player 6-test.JSONTest$Player@6385cb26], Team 4-test.JSONTest$Team@15d9bc04:[Player 7-test.JSONTest$Player@38364841, Player 8-test.JSONTest$Player@28c4711c]]
到目前为止一切顺利,那么为什么要“接近要求的东西”呢? 我不得不稍微更改输入 json,以便 Jackson 正确识别团队中的球员是对球员列表中球员的引用:
{
"players": [
{ "name": "Player 1" },
{ "name": "Player 2" },
{ "name": "Player 3" },
{ "name": "Player 4" },
{ "name": "Player 5" },
{ "name": "Player 6" },
{ "name": "Player 7" },
{ "name": "Player 8" }
],
"teams": [
{
"name": "Team 1",
"players": [ "Player 1", "Player 2"]
},
{
"name": "Team 2",
"players": [ "Player 3", "Player 4"]
},
{
"name": "Team 3",
"players": [ "Player 5", "Player 6"]
},
{
"name": "Team 4",
"players": [ "Player 7", "Player 8"]
}
]
}
关于java - jackson :引用同一个对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37640985/