如何在Go中自定义JSON编码输出?

huangapple go评论99阅读模式
英文:

How to customize JSON encoding output in Go?

问题

我想自定义一个结构体的编码格式,但是遇到了错误:json: error calling MarshalJSON for type main.Info: invalid character 'o' in literal false (expecting 'a')。我的代码有什么问题?

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))
}

你的代码有一个问题,即结构体字段的名称应该以大写字母开头,以便在JSON编码时能够访问到它们。你可以将nameflag字段改为NameFlag,然后再尝试运行代码。

英文:

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))
}

答案1

得分: 3

你的代码生成了无效的JSON文本。

你应该写上字段的名称,并为安全起见,引用字段名称和string值,但不要引用bool值(否则Go的json包将无法将其解组为bool)。同时,将你的值用大括号{}括起来,像这样:

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(`}`))               // 必须以 } 结尾

输出(在Go Playground上尝试完整的应用程序):

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

但是,如果在编组期间没有进行任何特殊处理,只需导出字段(以大写字母开头),json包将自动为您进行编组/解组:

type Info struct {
	Name string
	Flag bool
}

Go Playground上尝试这个版本。

输出(注意大写的名称"Name""Flag"):

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

如果你想在JSON文本中使用不同的名称,你还可以使用标签:

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

这将再次生成一个带有小写名称的输出:

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

阅读json.Marshal()函数的文档,了解有关结构标签的其他选项和自定义内容。

英文:

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.

答案2

得分: 0

将自己的字符串写入缓冲区有点违背了json.Marshal()的目的。也许可以尝试以下代码:

type Info struct {
    Name string `json:"name"` // 结构体字段的首字母应大写,这样才能被编组器找到。
    Flag bool   `json:"flag"` // json:"flag" 将对象键分配为 JSON 格式。
}

func main() {
    myInfo := Info{
        Name: "Foo",
        Flag: true,
    }
    slc, _ := json.Marshal(myInfo)
    fmt.Println(string(slc))
}

这样做可以将结构体编组为 JSON 字符串并打印出来。

英文:

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))

答案3

得分: 0

问题在于你期望在MarshalJSON的实现中有很多神奇的事情发生。不幸的是,你需要将其视为完全从头开始构建字符串。意思是这样的:

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 (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(`}`))
}

另外,我假设你真的想要一个字符串来表示布尔值(否则为什么要实现这个方法呢?),如果不是的话,你可以去掉if语句,直接使用b.Write([]byte(i.flag))

英文:

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)).

huangapple
  • 本文由 发表于 2015年4月21日 18:13:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/29769234.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定