Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to customize JSON encoding output in Go?

Tags:

json

go

I want to customize encoding format of a struct but got error: json: error calling MarshalJSON for type main.Info: invalid character 'o' in literal false (expecting 'a') What's wrong with my code?

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "log"
)

type Info struct {
    name string
    flag bool
}

func (i Info) MarshalJSON() ([]byte, error) {
    var b bytes.Buffer
    b.Write([]byte(i.name))
    if i.flag {
        b.Write([]byte(`"true"`))
    } else {
        b.Write([]byte(`"false"`))
    }   
    return b.Bytes(), nil 
}

func main() {
    a := []Info{
        {"foo", true},
        {"bar", false},
    }   
    out, err := json.Marshal(a)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf(string(out))
}
like image 831
Meng Avatar asked Apr 21 '15 10:04

Meng


People also ask

How do I encode a go map in a JSON object?

JSON objects only support strings as keys; to encode a Go map type it must be of the form map T (where T is any Go type supported by the json package).

What is the use of decoder and encoder in JSON?

The json package provides Decoder and Encoder types to support the common operation of reading and writing streams of JSON data. The NewDecoder and NewEncoder functions wrap the io.Reader and io.Writer interface types.

How to indent JSON data in Go language?

The Go library is following the spec. The easiest way to do this is with MarshalIndent, which will let you specify how you would like it indented via the indent argument. Thus, json.MarshalIndent (data, "", " ") will pretty-print using four spaces for indentation.

What types of data can be encoded in map JSON?

JSON objects only support strings as keys; to encode a Go map type it must be of the form map [string]T (where T is any Go type supported by the json package). Channel, complex, and function types cannot be encoded. Cyclic data structures are not supported; they will cause Marshal to go into an infinite loop.


1 Answers

Your code produces invalid JSON text.

You should write the names of the fields too, and for safety quote both the field names and string values but don't quote bool values (else the Go json package will not unmarshal it into bool for example). Also enclose your values in brackets {} like this:

b.Write([]byte(`{"name":"`))       // started with {
b.Write([]byte(i.name))
b.Write([]byte(`","flag":`))       // note the , between values
if i.flag {
    b.Write([]byte(`true`))        // don't quote boolean value
} else {
    b.Write([]byte(`false`))       // don't quote boolean value
}
b.Write([]byte(`}`))               // must close with }

Output (try the complete application on the Go Playground):

[{"name":"foo","flag":true},{"name":"bar","flag":false}]

But since you're not doing anything special during marshal, just export the fields (by starting them with upper-case letters) and the json package will marshal/unmarshal it for you automatically:

type Info struct {
    Name string
    Flag bool
}

Try this version on the Go Playground.

Output (note the upper-cased names "Name" and "Flag"):

[{"Name":"foo","Flag":true},{"Name":"bar","Flag":false}]

You can also use tags if you want to use different names in the JSON text like this:

type Info struct {
    Name string `json:"name"`
    Flag bool   `json:"flag"`
}

This will again produce an output with lower-cased names:

[{"name":"foo","flag":true},{"name":"bar","flag":false}]

Read the documentation of the json.Marshal() function to see what other options and customizations you can do with struct tags.

like image 167
icza Avatar answered Sep 23 '22 06:09

icza