Rest – How to represent object references in JSON

apidesignjsonrestweb services

I am trying to figure out what's the best approach when dealing with object references in a JSON to be sent to my server for deserialization.

To clarify, what I mean is how to refer to data contained in the JSON body itself.

For the example let's use the following (incorrect) JSON body:

{
  "players": [
    { "name": "Player 1" },
    { "name": "Player 2" },
    { "name": "Player 3" },
    { "name": "Player 4" }
  ],
  "teams": [
    {
      "name": "Team 1",
      "players": [ player1, player2 ],
    },
    {
      "name": "Team 2",
      "players": [ player3, player4 ],
    },
  ]
}

I need some means to refer to those playerX, which would represent the players sent in the players list (Player 1, Player 2, etc…), as you can imagine.

I can think of three different ways of doing this and none satisfy me:

The first way is to simply refer to the player as its position in the players list, this way no extra information is needed in the JSON body. For example, if we had "teams": [ [ 0, 1 ], [ 2, 3 ] ] that would mean we have two teams, the first one composed by P1 and P2 and the second one by P3 and P4.

The other two ways would both use IDs (I would need to implement an ID system for my players, since so far I haven't been in the need of IDs). However, since the players don't exist in the system yet, their IDs are unknown. As I see, we can do two things:

We can explicitly send the player IDs to the server, but we would have to develop ways of preserving ID uniqueness. I would prefer if these were not handled by the user, to by honest, as well as autogenerated. The way we would represent the players in the teams list would look the same like the first choice in format, but instead of the positions, we would have the IDS:

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

The second option is safer in terms of ID generation, but it requires two steps. First we send the player data to the server, then the user can ask the server for the players information and check their IDs. This way they create the teams safely because the ID was generated by the server.

Step 1. Send players.

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

Step 2. Query players. GET /players

{
  "players": [
    { "id": 11, "name": "Player 1" },
    { "id": 12, "name": "Player 2" },
    { "id": 13, "name": "Player 3" },
    { "id": 14, "name": "Player 4" }
  ]
}

Step 3. Send teams.

{
  "teams": [
      [ 11, 12 ], [ 13, 14 ] 
  ]
}

So how is this usually dealt with? How do JSON API developers tackle this scenario?

Best Answer

For inspiration, you may want to look into the way some of the json based api's (ex: json api, HAL) handle embedding.

One simple answer is to track your data in a key value store. For example

{ "/players/0" : {...}
, "/players/1" : {...}
, "/players/2" : {...}
, "/players/3" : {...}
, "/teams/0" : {...}
, "/teams/1" : {...}
}

And then you describe the players assigned to your team using local references

, "/teams/0" :
    { refs : 
        [ "/players/0"
        , "/players/1"
        ]
    }

As it happens, this scheme covers the case where you have identifiers too. Or where you only have some identifiers

, "/teams/0" :
    { refs : 
        [ "/players/0"
        , "/players/2ad8cabe-2f93-11e6-ac61-9e71128cae77"
        ]
    }

There are fancier versions of this idea (see the links).

That said, I've been down this road myself, and I really tied myself in knots until I concluded: if what you really have is a list of names, rather than a list of players, admit that to yourself, code it that way, and deal with it. It's the more honest way of representing what's going on in the domain at that point in time.

In which case, the payload of your message should look very close to:

{ "Team 1" : 
  [ "Player 1"
  , "Player 2"
  ]
, "Team 2" :
  [ "Player 3"
  , "Player 4"
  ]
}

If that makes you twitchy, remember: this isn't a description of your domain objects; it's a message. The changes it makes to your domain are a side effect. Jim Webber covers this in the introduction to this talk.

Related Topic