API Design: Undocumented object keys vs. Array of Objects

Viewed 904

For this JSON API design problem, I have an arbitrary set of key-value pairs that need to be provided in the API Request/Response bodies. Both the keys and values are unknown. What is the best way to structure this?

As far as I can tell, there are two ways to accomplish this:

1. Undocumented object keys

{
    "fruit": "Apple",
    "sport": "Hockey",
    ...
    "keyN": "valueN"
}
  • PROS: Very clean, easy for application logic to parse
  • CONS: This object can't be documented properly - the shape of the object is infinitely arbitrary.

2. Array of Objects

[
    {
        "key": "fruit",
        "value": "Apple"
    },
    {
        "key": "sport",
        "value": "Hockey"
    },
    ...
    {
        "key": "keyN",
        "value": "valueN"
    }
]
  • PROS: Easy to document and understand as an array of objects with a known structure.
  • CONS: Application logic will be more verbose.

What is the best way to structure this?


NOTE: This is a question about API documentation, not about application logic. As noted above, I'm well aware that #1 is the best solution for manipulation in code. But it's unclear to me just yet how to document this in API docs in such a way that will always be interpreted correctly

3 Answers

I very, very strongly recommend #1. #2 is a perversion. Your data structure is a dictionary. Don't use an array to implement a dictionary, but with half the features.

Think about it: You say you cannot document #1. So how are you going to document #2? If #1 mustn't contain a key "strawberry", how do you document that #2 mustn't contain a dictionary with a (key, value) pair of key = "key", value = "strawberry"?

How do you check whether #1 or #2 contains a key "fruit", and what the value is? #1 is a direct access. dict ["fruit"]. In #2 you need to iterate through the array elements, check that they are all dictionaries, check if there is one with an entry key: "fruit", check that it has another entry "value". Maybe if you are paid by lines of code you would do that.

Funny enough, three completely different answers, each with a downvote. Obviously at least two downvoters are stupid.

Personally I don't see much difference between #1 and #2 in terms of documentation. If the fields are unknown then an empty map/object vs an empty array doesn't really matter.

I've seen this referred to as a 'Json Junk Drawer'.

One addition I have seen is a named map/object specific for the 'Json Junk Drawer' like:

{
  "fieldsThatDontChange" : "example",
  "attributes" :{
    "unknownfield" : "unknown"
  }
}

It's all an anti-pattern really. But one small benefit here is you can tell clients that the attributes section is where to put their undocumented stuff. Maybe even enforce client specific schemas on the attributes section if that's a use-case for example.

Here is some more info on JSON junk drawers which has some links to youtube videos discussing the subject:

http://apievangelist.com/2015/01/21/rest-api-design-bridging-what-we-have-to-the-future-by-organizing-the-json-junk-drawer/

The better solution is:

2. Array of Objects

[
    {
        "key": "fruit",
        "value": "Apple"
    },
    {
        "key": "sport",
        "value": "Hockey"
    },
    ...
    {
        "key": "keyN",
        "value": "valueN"
    }
]

Arrays are exactly the correct pattern to use when there is arbitrary length involved. This is more verbose but easier to understand. Favour verbosity.

Related