杰克逊:引用相同的对象

时间:2016-06-05 10:46:44

标签: java json jackson

在某些情况下,正文中序列化或反序列化的数据(例如,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"}
      ]
    }
  ]
}

您可以想象 Player X 指的是同一个对象,但可能最终会出现一个不需要的场景,其中 Player X 由不同的对象表示。

我想知道这些方案的最佳和最常用的方法是什么。我可以想到几种方法:

  1. Player课程添加ID属性。我的设计不包含ID,因为它不需要。识别对象的方式是通过引用,如果它们包含在集合中,则通过它们在其中的位置(如果集合具有位置)。这可能被认为是一种不好的做法,但我故意这样做,除非必要,否则我不打算改变它。
  2. 还有一个玩家ID,但不是将球队序列化/反序列化为球员列表,而只是一个ID列表。 JSON正文中包含的信息将丢失,但我们会有更紧凑的数据。
  3. 坚持我的无ID设计,与前一点类似,我可以有一个位置列表(实际上实际上是一个玩家ID,因为它们用作识别手段),所以数字 0 < / em>将引用 player 列表中第一个位置的玩家。
  4. 更改合约并让玩家拥有唯一名称。有了这个,我们现在可以拥有一个ID而无需向该类添加新属性。但是我认为这是一个坏主意,因为不一定所有球员都应该有不同的名字。
  5. 最好的方法是什么?通常做什么?你有不同的建议吗?

2 个答案:

答案 0 :(得分:5)

我达到了接近问题要求的东西: 我使用了杰克逊的Object Identity Feature,它允许定义某个属性的值来识别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();
    }
}

以下是使用的POJO的其余部分:

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]]

输出清楚地表明在玩家和团队中使用相同的对象:

{
  "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"]
    }
  ]
}

到目前为止一直很好,所以为什么&#34;接近要求的东西&#34;? 我不得不稍微改变输入json,以便杰克逊能够正确识别出球队中的球员是否参考了球员名单中的球员:

$ba = 'whatever';    
    $results = 
DB::select(DB::raw("SELECT 
          t.id, t.AvgStyle, r.RateDesc
          FROM (
               SELECT
               p.id, ROUND(AVG(s.Value)) AS AvgStyle
               FROM posts p
               INNER JOIN styles s
               ON s.post_id = p.id  
               WHERE author = ?    
               GROUP BY p.id
               ) t
               INNER JOIN rates r
                   ON r.digit = t.AvgStyle"
          , )[$ba]); // @everton We just needed to move it one parentheses

答案 1 :(得分:0)

我去了定位参考。显然,它不一定被认为是一种糟糕的方法。

所以JSON主体就是这样的:

{
  "players": [
    { "name": "Player 1" },
    { "name": "Player 2" },
    { "name": "Player 3" },
    { "name": "Player 4" }
  ],
  "teams": [
    [ 3, 1 ],
    [ 0, 2 ]
  ]
}