@@ -60,6 +60,11 @@ func newErrUnsupportedPtrType(rf reflect.Value, t reflect.Type, structField refl
6060 return ErrUnsupportedPtrType {rf , t , structField }
6161}
6262
63+ type includedNode struct {
64+ node * Node
65+ model * reflect.Value
66+ }
67+
6368// UnmarshalPayload converts an io into a struct instance using jsonapi tags on
6469// struct fields. This method supports single request payloads only, at the
6570// moment. Bulk creates and updates are not supported yet.
@@ -94,19 +99,19 @@ func newErrUnsupportedPtrType(rf reflect.Value, t reflect.Type, structField refl
9499// model interface{} should be a pointer to a struct.
95100func UnmarshalPayload (in io.Reader , model interface {}) error {
96101 payload := new (OnePayload )
102+ included := make (map [string ]* includedNode )
97103
98104 if err := json .NewDecoder (in ).Decode (payload ); err != nil {
99105 return err
100106 }
101107
102108 if payload .Included != nil {
103- includedMap := make (map [string ]* Node )
104- for _ , included := range payload .Included {
105- key := fmt .Sprintf ("%s,%s" , included .Type , included .ID )
106- includedMap [key ] = included
109+ for _ , include := range payload .Included {
110+ key := fmt .Sprintf ("%s,%s" , include .Type , include .ID )
111+ included [key ] = & includedNode {include , nil }
107112 }
108113
109- return unmarshalNode (payload .Data , reflect .ValueOf (model ), & includedMap )
114+ return unmarshalNode (payload .Data , reflect .ValueOf (model ), & included )
110115 }
111116 return unmarshalNode (payload .Data , reflect .ValueOf (model ), nil )
112117}
@@ -120,19 +125,19 @@ func UnmarshalManyPayload(in io.Reader, t reflect.Type) ([]interface{}, error) {
120125 return nil , err
121126 }
122127
123- models := []interface {}{} // will be populated from the "data"
124- includedMap := map [string ]* Node {} // will be populate from the "included"
128+ models := []interface {}{} // will be populated from the "data"
129+ included := map [string ]* includedNode {} // will be populate from the "included"
125130
126131 if payload .Included != nil {
127- for _ , included := range payload .Included {
128- key := fmt .Sprintf ("%s,%s" , included .Type , included .ID )
129- includedMap [key ] = included
132+ for _ , include := range payload .Included {
133+ key := fmt .Sprintf ("%s,%s" , include .Type , include .ID )
134+ included [key ] = & includedNode { include , nil }
130135 }
131136 }
132137
133138 for _ , data := range payload .Data {
134139 model := reflect .New (t .Elem ())
135- err := unmarshalNode (data , model , & includedMap )
140+ err := unmarshalNode (data , model , & included )
136141 if err != nil {
137142 return nil , err
138143 }
@@ -263,7 +268,7 @@ func getStructTags(field reflect.StructField) ([]string, error) {
263268
264269// unmarshalNodeMaybeChoice populates a model that may or may not be
265270// a choice type struct that corresponds to a polyrelation or relation
266- func unmarshalNodeMaybeChoice (m * reflect.Value , data * Node , annotation string , choiceTypeMapping map [string ]structFieldIndex , included * map [string ]* Node ) error {
271+ func unmarshalNodeMaybeChoice (m * reflect.Value , data * Node , annotation string , choiceTypeMapping map [string ]structFieldIndex , included * map [string ]* includedNode ) error {
267272 // This will hold either the value of the choice type model or the actual
268273 // model, depending on annotation
269274 var actualModel = * m
@@ -300,7 +305,7 @@ func unmarshalNodeMaybeChoice(m *reflect.Value, data *Node, annotation string, c
300305 return nil
301306}
302307
303- func unmarshalNode (data * Node , model reflect.Value , included * map [string ]* Node ) (err error ) {
308+ func unmarshalNode (data * Node , model reflect.Value , included * map [string ]* includedNode ) (err error ) {
304309 defer func () {
305310 if r := recover (); r != nil {
306311 err = fmt .Errorf ("data is not a jsonapi representation of '%v'" , model .Type ())
@@ -509,6 +514,23 @@ func unmarshalNode(data *Node, model reflect.Value, included *map[string]*Node)
509514 // model, depending on annotation
510515 m := reflect .New (fieldValue .Type ().Elem ())
511516
517+ // Check if the item in the relationship was already processed elsewhere. Avoids potential infinite recursive loops
518+ // caused by circular references between included relationships (two included items include one another)
519+ includedKey := fmt .Sprintf ("%s,%s" , relationship .Data .Type , relationship .Data .ID )
520+ if included != nil && (* included )[includedKey ] != nil {
521+ if (* included )[includedKey ].model != nil {
522+ fieldValue .Set (* (* included )[includedKey ].model )
523+ } else {
524+ (* included )[includedKey ].model = & m
525+ err := unmarshalNodeMaybeChoice (& m , (* included )[includedKey ].node , annotation , choiceMapping , included )
526+ if err != nil {
527+ er = err
528+ break
529+ }
530+ fieldValue .Set (m )
531+ }
532+ continue
533+ }
512534 err = unmarshalNodeMaybeChoice (& m , relationship .Data , annotation , choiceMapping , included )
513535 if err != nil {
514536 er = err
@@ -565,11 +587,11 @@ func unmarshalNode(data *Node, model reflect.Value, included *map[string]*Node)
565587 return er
566588}
567589
568- func fullNode (n * Node , included * map [string ]* Node ) * Node {
590+ func fullNode (n * Node , included * map [string ]* includedNode ) * Node {
569591 includedKey := fmt .Sprintf ("%s,%s" , n .Type , n .ID )
570592
571593 if included != nil && (* included )[includedKey ] != nil {
572- return (* included )[includedKey ]
594+ return (* included )[includedKey ]. node
573595 }
574596
575597 return n
0 commit comments