Another use case for attributes in relationship objects


#1

This is in support of and expanding on these discussions:


I want to express relationships between words in a language (learning) API. Just to review our grammar:

Verb infinitive: to go
3rd person singular present: he/she/it goes
3rd person singular past: he/she/it went

If I want to express in a document the relationship between these words, it would be natural to use the relationships field.

To wit:

{
    "data":{
        "id":"to go",
        "type":"verb",
        "relationships":{
            "conjugations":{
                "data":[
                    {
                        "id":"goes",
                        "type":"verb",
                        "attributes":{
                            "tense":"present",
                            "person":3,
                            "number":"singular"
                        }
                    },
					{
                        "id":"went",
                        "type":"verb",
                        "attributes":{
                            "tense":"past",
                            "person":3,
                            "number":"singular"
                        }
                    }
                ]
            }
        }
    }
}

The prototypical use case for the jsonapi 1.0 spec seems to make the assumption that any and all relationships can be (and should be) clearly stated by a single field (e.g. “father”) rather than as a set of attributes (e.g. “parent”, “adoptive”, “male”, “step”). To have universal applicability, I really think this restriction should be reconsidered. In short, a relationship often needs to be described. An ‘author’ has a clear relationship to a ‘book’, but what is the relationship of the word ‘go’ to the verb ‘to go’? The answer is ‘it depends’.

To expand, jsonapi 1.0 spec would have the word attributes in the example included in a resource object like so:

"included":[
    { 
	    id: "goes",
	    type: "verb",
	    "attributes":{
			"tense":"present",
			"person":3,
			"number":"singular"
		}
    },
	{
		"id":"went",
		"type":"verb",
		"attributes":{
			"tense":"past",
			"person":3,
			"number":"singular"
		}
	}		
]

But how do these following attributes fit into this resource object scheme?
1st person singular present: I go
2nd person plural present: we go

"included":[
    { 
	    id: "go",
	    type: "verb",
	    "attributes":{/* what goes here?? This isn't the place for it. */}
	}

For English, it may be easy (though awkward) enough to do:

            "conjugations":{
                "data":{
                     "present-1person-singular":{
                        "id":"go",
                        "type":"verb"
                    },
                    "present-2person-plural":{
                        "id":"go",
                        "type":"verb"
                    }
                }
            }

But this still forces the client to parse the key to understand the relationship of these words, and it doesn’t really work for, specifically, languages like Finnish which can have over a hundred such relationships from a single base word; nor for any of the literally infinite other phenomena in the Universe for which structured relationships cannot be captured easily using a single field (is an ‘author’ also a ‘father’?).

Or, you could do:

"included": {
"conjugations":[
	{"id":"present-1person-singular",
	"type":conjugation",
	"attributes":{  
                    "tense":"present",
                    "person":1,
                    "number":"singular"
                }
	} /// etc...
]

}

and then:

            "conjugations":[
				{
                    "id":"go",
					"type":"present-1person-singular"
				},
				{	"id":"go",
					"type":"present-2person-singular"
				}                         /// etc...
			]

But at this point, you’re struggling with the spec. It’s working against you, not for you.

I’m tempted to do this, wherein if “meta” contains an “attributes” field, it’s essentially the same as putting “attributes” into the resource identifier, but without breaking spec. A client using the API would just need to be aware. A kind of informal spec, perhaps.

Thoughts?

"conjugations":{  
    "data":[  
        {  
            "id":"goes",
            "type":"verb",
            "meta":{  
                "attributes":{  
                    "mood":"indicative",
                    "tense":"present",
                    "polarity":"positive",
                    "person":3,
                    "number":"singular"
                }
            }
        },
        {  
            "id":"went",
            "type":"verb",
            "meta":{  
                "attributes":{  
                    "mood":"indicative",
                    "tense":"past",
                    "polarity":"positive",
                    "person":3,
                    "number":"singular"
                }
            }
        }
    ]
}

#2

Rendall, check out my blog on guidelines for hypermedia here. I’m only halfway through the explanation posts, but the ones remaining are actually out of scope to your question. Since my solution to your problem isn’t fully baked yet, I can’t suggest you use it. However, I think it’s a succinct guide to walk you through how to attack a hypermedia API.

If you’re still in design mode it goes into some very helpful constraints which will answer a lot of this for you.

The reason I suggest this, is you are trying to define your vocabulary as part of your resource representation, and this will cause insane headaches for you down the road, even bigger than the one you’re looking at now. Relationships in JSON Api are essentially the unit of composition description for to compose larger contexts for the resources. If you are having trouble, or explain the relationship of x to y as ‘it depends’ then you haven’t completed the step of making your resources atomic. If extra information is needed for the ‘it depends’ why not just create another resource to describe the state, and you can include the descriptions transitively in that resources relationships/includes section.

Attributes in your example are the data, for JSON Api you have essentially 3 buckets in a resource representation’s serialization. The ‘data’ which is composed of all native data (string, arrays, numbers…etc.), the relationships for holding the relationship objects which describe the relationship between 2 resources (no data), or the include section which brings in the full resource available at a specific URI into the message to actually be part of the compound document.

If you want to include the other resources, you use the “included” section, if you want to just describe and point to other resources you use the relationships section.


How is OData related to JSON:API?