Skip to content
This repository was archived by the owner on Sep 4, 2025. It is now read-only.
This repository was archived by the owner on Sep 4, 2025. It is now read-only.

Add links/meta to the top document #93

Closed

Description

@mbn18

Hi, I cant seems to create a response like the one bellow (origin JsonAPI site)

{
    "meta": {
        "total-pages": 13
    },
    "data": [{
        "type": "articles",
        "id": "3",
        "attributes": {
            "title": "JSON API paints my bikeshed!",
            "body": "The shortest article. Ever.",
            "created": "2015-05-22T14:56:29.000Z",
            "updated": "2015-05-22T14:56:28.000Z"
        }
    }],
    "links": {
        "self": "http://example.com/articles?page[number]=3&page[size]=1",
        "first": "http://example.com/articles?page[number]=1&page[size]=1",
        "prev": "http://example.com/articles?page[number]=2&page[size]=1",
        "next": "http://example.com/articles?page[number]=4&page[size]=1",
        "last": "http://example.com/articles?page[number]=13&page[size]=1"
    }
}

I can get the data but could not figure how to add meta or links to the response. I could only add them under the data level

This is the struct

type Album struct {
	ID   int    `jsonapi:"primary,album"`
	Name string `jsonapi:"attr,name"`
}

First I tried []*Album but coludnt understand how to add meta/links to the slice
Then I created a wrapper struct

type Albums struct {
	Albums []*Album `jsonapi:"relation,albums"`
}

But the output was invalid (or at least not like the provided example)

What am I missing. How can I add meta/links to the top level. Thanks.

Activity

  1. self-assigned this
    on Jun 28, 2017
  2. aren55555 commented on Jun 28, 2017

    @aren55555
    Contributor

    You can do something like the following:

    func main() {
    	albums := []*Album{
    		&Album{ID: 3, Name: "JSON API paints my bikeshed!"},
    	}
    
    	var m []interface{}
    	for _, a := range albums {
    		m = append(m, a)
    	}
    	payload, err := jsonapi.MarshalMany(m)
    	if err != nil {
    		panic(err)
    	}
    
    	payload.Meta = &jsonapi.Meta{
    		"total-pages": 13,
    	}
    
    	payload.Links = &jsonapi.Links{
    		"self":  "http://example.com/articles?page[number]=3&page[size]=1",
    		"first": "http://example.com/articles?page[number]=1&page[size]=1",
    		"prev":  "http://example.com/articles?page[number]=2&page[size]=1",
    		"next":  "http://example.com/articles?page[number]=4&page[size]=1",
    		"last":  "http://example.com/articles?page[number]=13&page[size]=1",
    	}
    
    	w := bytes.NewBuffer(nil)
    	if err := json.NewEncoder(w).Encode(payload); err != nil {
    		panic(err)
    	}
    	fmt.Println(w)
    }

    Which will print the following JSON:

    {
        "data": [{
            "type": "album",
            "id": "3",
            "attributes": {
                "name": "JSON API paints my bikeshed!"
            }
        }],
        "links": {
            "first": "http://example.com/articles?page[number]=1&page[size]=1",
            "last": "http://example.com/articles?page[number]=13&page[size]=1",
            "next": "http://example.com/articles?page[number]=4&page[size]=1",
            "prev": "http://example.com/articles?page[number]=2&page[size]=1",
            "self": "http://example.com/articles?page[number]=3&page[size]=1"
        },
        "meta": {
            "total-pages": 13
        }
    }
  3. imhuytq commented on Aug 3, 2017

    @imhuytq

    @aren55555 Now, MarshalMany isn't public function, how can I use it?

  4. aren55555 commented on Aug 3, 2017

    @aren55555
    Contributor

    @pp4p since posting that sample code the Marshal API has been significantly simplified. You no longer need to populate a slice of interfaces []interface{}. Furthermore, Marshal first return will be a value that implements the Payloader interface. Since we passed Marshal a slice, the Payloader returned will always be a *jsonapi.ManyPayload.

    You can now get away with just doing main.go:

    package main
    
    import (
    	"bytes"
    	"encoding/json"
    	"fmt"
    
    	"github.com/google/jsonapi"
    )
    
    type Album struct {
    	ID   int    `jsonapi:"primary,album"`
    	Name string `jsonapi:"attr,name"`
    }
    
    type Albums struct {
    	Albums []*Album `jsonapi:"relation,albums"`
    }
    
    func main() {
    	albums := []*Album{
    		&Album{ID: 3, Name: "JSON API paints my bikeshed!"},
    	}
    
    	p, err := jsonapi.Marshal(albums)
    	if err != nil {
    		panic(err)
    	}
    
    	payload, ok := p.(*jsonapi.ManyPayload)
    	if !ok {
    		panic(fmt.Errorf("was not a many payloader"))
    	}
    
    	payload.Meta = &jsonapi.Meta{
    		"total-pages": 13,
    	}
    
    	payload.Links = &jsonapi.Links{
    		"self":  "http://example.com/articles?page[number]=3&page[size]=1",
    		"first": "http://example.com/articles?page[number]=1&page[size]=1",
    		"prev":  "http://example.com/articles?page[number]=2&page[size]=1",
    		"next":  "http://example.com/articles?page[number]=4&page[size]=1",
    		"last":  "http://example.com/articles?page[number]=13&page[size]=1",
    	}
    
    	w := bytes.NewBuffer(nil)
    	if err := json.NewEncoder(w).Encode(payload); err != nil {
    		panic(err)
    	}
    	fmt.Println(w)
    }

    go run main.go will produce:

    {
        "data": [{
            "type": "album",
            "id": "3",
            "attributes": {
                "name": "JSON API paints my bikeshed!"
            }
        }],
        "links": {
            "first": "http://example.com/articles?page[number]=1\u0026page[size]=1",
            "last": "http://example.com/articles?page[number]=13\u0026page[size]=1",
            "next": "http://example.com/articles?page[number]=4\u0026page[size]=1",
            "prev": "http://example.com/articles?page[number]=2\u0026page[size]=1",
            "self": "http://example.com/articles?page[number]=3\u0026page[size]=1"
        },
        "meta": {
            "total-pages": 13
        }
    }
  5. imhuytq commented on Aug 3, 2017

    @imhuytq

    @aren55555 thank you.

  6. mrhwick commented on Nov 29, 2017

    @mrhwick

    I thought about submitting a PR to implement this in the library, since the official site recommends top level links be included alongside resource-level and relationship links. I think it's important to keep the library simple, so I decided against writing this feature into the library. Would it make sense to write some documentation for how to achieve this recommended functionality.

  7. rberlind commented on Nov 9, 2020

    @rberlind

    The solution provided here only addresses marshaling links into a structure. We also need a solution for unmarshalling links from a structure that uses jsonapi structure tags for other fields. Adding additional links/meta tags to the top document as the original poster requested would really make the jsonapi much easier to use. And while an example of the solution given in #68 is provided in https://github.com/google/jsonapi/blob/master/request_test.go#L694, that example does not show how to demarshal a structure that uses jsonapi structure tags.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions