Reputation: 807
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))
}
Upvotes: 6
Views: 7535
Reputation: 418465
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.
Upvotes: 3
Reputation: 48124
The problem is that you're expecting a lot to magically happen for you within your implementation of MarshalJSON
. Unfortunately you need to look at it as though you're building the string entirely from scratch. Meaning this;
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
}
Needs to look more like this;
func (i Info) MarshalJSON() ([]byte, error) {
var b bytes.Buffer
b.Write([]byte(`{"name":"`))
b.Write([]byte(i.name))
b.Write([]byte(`","flag":`))
if i.flag {
b.Write([]byte(`"true"`))
} else {
b.Write([]byte(`"false"`))
}
b.Write([]byte(`}`))
}
Also, I'm assuming you legitimately want a string for the boolean values (otherwise why implement this method at all, right?), if not then you'll want to get rid of the if and just use b.Write([]byte(i.flag))
.
Upvotes: 0
Reputation: 28927
Writing your own strings to a buffer kind of eliminates the purpose of json.Marshal()
. Maybe something like:
type Info struct {
Name string `json:"name"` // Struct fields should have capital first letter
// if you want it to be found by the marshaller.
Flag bool `json:"flag"` // json:"flag" assigns the object key in json format
}
And then something like:
myInfo := Info {
Name: "Foo",
Flag: true,
}
slc, _ := json.Marshal(myInfo)
fmt.Println(string(slc))
Upvotes: 0