1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
|
package jsonapi
import (
"bytes"
"encoding/json"
"errors"
)
var objectSuffix = []byte("{")
var arraySuffix = []byte("[")
var stringSuffix = []byte(`"`)
// A Document represents a JSON API document as specified here: http://jsonapi.org.
type Document struct {
Links Links `json:"links,omitempty"`
Data *DataContainer `json:"data"`
Included []Data `json:"included,omitempty"`
Meta map[string]interface{} `json:"meta,omitempty"`
}
// A DataContainer is used to marshal and unmarshal single objects and arrays
// of objects.
type DataContainer struct {
DataObject *Data
DataArray []Data
}
// UnmarshalJSON unmarshals the JSON-encoded data to the DataObject field if the
// root element is an object or to the DataArray field for arrays.
func (c *DataContainer) UnmarshalJSON(payload []byte) error {
if bytes.HasPrefix(payload, objectSuffix) {
return json.Unmarshal(payload, &c.DataObject)
}
if bytes.HasPrefix(payload, arraySuffix) {
return json.Unmarshal(payload, &c.DataArray)
}
return errors.New("expected a JSON encoded object or array")
}
// MarshalJSON returns the JSON encoding of the DataArray field or the DataObject
// field. It will return "null" if neither of them is set.
func (c *DataContainer) MarshalJSON() ([]byte, error) {
if c.DataArray != nil {
return json.Marshal(c.DataArray)
}
return json.Marshal(c.DataObject)
}
// Link represents a link for return in the document.
type Link struct {
Href string `json:"href"`
Meta map[string]interface{} `json:"meta,omitempty"`
}
// UnmarshalJSON marshals a string value into the Href field or marshals an
// object value into the whole struct.
func (l *Link) UnmarshalJSON(payload []byte) error {
if bytes.HasPrefix(payload, stringSuffix) {
return json.Unmarshal(payload, &l.Href)
}
if bytes.HasPrefix(payload, objectSuffix) {
obj := make(map[string]interface{})
err := json.Unmarshal(payload, &obj)
if err != nil {
return err
}
var ok bool
l.Href, ok = obj["href"].(string)
if !ok {
return errors.New(`link object expects a "href" key`)
}
l.Meta, _ = obj["meta"].(map[string]interface{})
return nil
}
return errors.New("expected a JSON encoded string or object")
}
// MarshalJSON returns the JSON encoding of only the Href field if the Meta
// field is empty, otherwise it marshals the whole struct.
func (l Link) MarshalJSON() ([]byte, error) {
if len(l.Meta) == 0 {
return json.Marshal(l.Href)
}
return json.Marshal(map[string]interface{}{
"href": l.Href,
"meta": l.Meta,
})
}
// Links contains a map of custom Link objects as given by an element.
type Links map[string]Link
// Data is a general struct for document data and included data.
type Data struct {
Type string `json:"type"`
ID string `json:"id"`
Attributes json.RawMessage `json:"attributes"`
Relationships map[string]Relationship `json:"relationships,omitempty"`
Links Links `json:"links,omitempty"`
}
// Relationship contains reference IDs to the related structs
type Relationship struct {
Links Links `json:"links,omitempty"`
Data *RelationshipDataContainer `json:"data,omitempty"`
Meta map[string]interface{} `json:"meta,omitempty"`
}
// A RelationshipDataContainer is used to marshal and unmarshal single relationship
// objects and arrays of relationship objects.
type RelationshipDataContainer struct {
DataObject *RelationshipData
DataArray []RelationshipData
}
// UnmarshalJSON unmarshals the JSON-encoded data to the DataObject field if the
// root element is an object or to the DataArray field for arrays.
func (c *RelationshipDataContainer) UnmarshalJSON(payload []byte) error {
if bytes.HasPrefix(payload, objectSuffix) {
// payload is an object
return json.Unmarshal(payload, &c.DataObject)
}
if bytes.HasPrefix(payload, arraySuffix) {
// payload is an array
return json.Unmarshal(payload, &c.DataArray)
}
return errors.New("Invalid json for relationship data array/object")
}
// MarshalJSON returns the JSON encoding of the DataArray field or the DataObject
// field. It will return "null" if neither of them is set.
func (c *RelationshipDataContainer) MarshalJSON() ([]byte, error) {
if c.DataArray != nil {
return json.Marshal(c.DataArray)
}
return json.Marshal(c.DataObject)
}
// RelationshipData represents one specific reference ID.
type RelationshipData struct {
Type string `json:"type"`
ID string `json:"id"`
}
|