JSON into Go html template?

I try to store the menu in a database and fill a Go html template with this. I have managed to fetch the JSON and the filling of Go html template. Independent of each other. But how do I transfer the JSON from the func main into the template func page?

package main

import (
    "encoding/json"
    "fmt"
    "html/template"
    "io/ioutil"
    "net/http"
    "os"
)

func main() {
    resp, err := http.Get("https://api3.go4webdev.org/menu/all")
    if err != nil {
        fmt.Println("No response")
    }
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body)) // how pass this to "func page" and populate the menu?
    page()
}

func page() {
    fmt.Println()
    t := template.Must(template.New("").Parse(templ))

    m := map[string]interface{}{}
    if err := json.Unmarshal([]byte(jsondata), &m); err != nil {
        panic(err)
    }

    if err := t.Execute(os.Stdout, m); err != nil {
        panic(err)
    }
}

const templ = `
<html><body>
   <ul>
     <li id={{.menu_id}}>{{.menu_txt}}</li>
     <li id={{.menu_id}}>{{.menu_txt}}</li>
   </ul>
</body></html>`

const jsondata = `{"menu_id":"1", "menu_txt": "Home"}`

And the result will be this:

[{"menu_icn":"","menu_id":"code","menu_lnk":"/code","menu_main":"code","menu_sort":1,"menu_txt":"Code Philosophy"},{"menu_icn":"","menu_id":"home","menu_lnk":"/home","menu_main":"home","menu_sort":1,"menu_txt":"Navigation”}, etc…]

<html><body>
   <ul>
     <li id=1>Home</li>
     <li id=1>Home</li>
   </ul>
</body>

When I try to pass the JSON data into func page(jsondata)…

...
	page(string(body))
}

func page(jsondata) {
....

…I get some errors

./main.go:23:11: undefined: jsondata
./main.go:28:34: undefined: jsondata

And if I add string after jsondata

	page(string(body))
}

func page(jsondata string) {

I got this errors:

cannot unmarshal array into Go value of type map[string]interface {}

Hi Sibert,
Can you send the body as an []byte?
In this way you don’t have to convert the data twice.

package main

import (
	"encoding/json"
	"fmt"
	"html/template"
	"io"
	"net/http"
	"os"
)

func main() {
	resp, err := http.Get("https://api3.go4webdev.org/menu/all")
	if err != nil {
		fmt.Println("No response")
	}
	defer resp.Body.Close()
	body, err := io.ReadAll(resp.Body)
	fmt.Println(string(body)) // how pass this to "func page" and populate the menu?
	page(body)
}

func page(jsondata []byte) {
	fmt.Println()
	t := template.Must(template.New("").Parse(templ))

	m := map[string]interface{}{}
	if err := json.Unmarshal([]byte(jsondata), &m); err != nil {
		panic(err)
	}

	if err := t.Execute(os.Stdout, m); err != nil {
		panic(err)
	}
}

const templ = `
<html><body>
   <ul>
     <li id={{.menu_id}}>{{.menu_txt}}</li>
     <li id={{.menu_id}}>{{.menu_txt}}</li>
   </ul>
</body></html>`

Your code gave this error:

panic: json: cannot unmarshal array into Go value of type map[string]interface {}

JSON is much of a standard both my API and foreign API. So I guess I have to learn how to deal with JSON. How do I convert the data twice?

Here is a play without API. Same error as always:

package main

import (
	"encoding/json"
	"html/template"
	"os"
)

func main() {
	t := template.Must(template.New("").Parse(templ))

	m := map[string]interface{}{}
	if err := json.Unmarshal([]byte(jsondata), &m); err != nil {
		panic(err)
	}

	if err := t.Execute(os.Stdout, m); err != nil {
		panic(err)
	}
}

const templ = `
<html><body>
{{ range . }}
   <li id={{.menu_id}}> {{ .menu_txt }}</li>
{{ end }}
</body></html>`

const jsondata = `[{"menu_id":"1", "menu_txt": "Home"},{"menu_id":"2", "menu_txt": "Prefs"}]`

panic: json: cannot unmarshal array into Go value of type map[string]interface {}

You can unmarshal a JSON array into a Go []interface, or you can unmarshal a JSON object into a Go map[string]interface{}. Better would be to use more specific Go types that match the schema of your JSON.

	m := map[string]interface{}{}
	if err := json.Unmarshal([]interface(jsondata), &m); err != nil {
		panic(err)
	}

syntax error: unexpected (, expecting {

Or how do you mean?

json: cannot unmarshal array into Go value of type map[string]interface {}

Do not work as far as I can see?

After some Googling, I found one solution:

var m []map[string]interface{}

The error messages says what the problem is. You are trying to unmarshal a json array into a map. I told you and showed you that you can unmarshal a json array into a slice. If your json is an object, then you can unmarshal it into a map. You need to have some understanding of the structure of the json.

Yes, []map[string]interface{} is a little more specific than []interface{}, and that should make for simpler code accessing the data.

I do understand the structure of JSON. But I am lost regarding Go semantics and syntax for read and write JSON in Go.

For an example this was confusing to me:
m := []map[string]interface{} //does NOT work
but
var m []map[string]interface{} //does work

m := []map[string]interface{} assigns the a type to the variable m because the right-hand side is a type of a slice of maps, not a value.
var m []map[string]interface{} declares the variable m to be of a type (of slice of maps). It is implicitly initialized to the zero-value of the type.
:= is a shorthand to declare a variable and initialize it. The variable’s value will be set to the value of the right-hand side, and it’s type will be inferred from the type of the right-hand side. A Tour of Go

When using json.Unmarshal, it is unnecessary to initialize the variable because Unmarshal will set the variable`s value.

2 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.