可以将map [string]字符串编组为json会返回错误吗?

DMa*_*yer 3 json dictionary marshalling go

说我有以下代码:

m := map[string]string{}
//... do stuff to the map
b, err := json.Marshal(m)
Run Code Online (Sandbox Code Playgroud)

在这种情况下有没有办法json.Marshal调用会返回错误?

我想知道是出于好奇,部分是考虑我是否需要担心错误检查.

icz*_*cza 7

由于任何有效值string都是有效密钥,而且JSON中的有效值(有关详细信息,请参阅JSON密钥名称中哪些字符有效/无效?),理论上它不会返回任何错误.

如果发生内存不足错误,json.Marshal()将无法返回,您的应用程序将以错误代码终止.

由于Go将string值存储为UTF-8编码的字节序列,因此存在无效的UTF-8编码字符串内容的问题.这也不会导致任何错误,因为Go将使用Unicode替换字符U + FFFD替换无效代码点,如下例所示:

m := map[string]string{"\xff": "a"}
data, err := json.Marshal(m)
fmt.Println(string(data), err)
Run Code Online (Sandbox Code Playgroud)

输出(在Go Playground上试试):

{"\ufffd":"a"} <nil>
Run Code Online (Sandbox Code Playgroud)

此行为记录在json.Marshal():

字符串值编码为强制为有效UTF-8的JSON字符串,用Unicode替换符号替换无效字节.

这可能是一个编组map[string]string绝不会返回一个错误,不过,你应该检查返回的错误,除非文档明确指出返回的error始终是nil(的文档json.Marshal()不会记录这样的行为).这种罕见的例子是rand.Read()其中记录了"它总是返回LEN(p)和一个零误差".

并且标准库也有可能存在错误,因此即使json包的实现可能无意在编组时返回任何错误map[string]string,但错误可能导致它仍然返回非nil错误.

另见相关问题:Go:json.Unmarshal什么时候会返回struct返回错误?

并发映射修改

为了完整起见,让我们讨论json.Marshal()一下map[string]string在传递给它时可能导致失败的另一个问题.

Go 1.6在运行时添加了一个轻量级的并发滥用映射检测,你可以在这里阅读更多相关信息:如何从并发映射写入中恢复?

这意味着Go运行时可以检测是否在goroutine中读取或修改了映射,并且还同时修改了另一个goroutine,而没有同步.

所以这里的场景是我们传递map[string]stringjson.Marshal().并且为了进行封送,json包必须显然迭代地图的键值.如果我们同时修改地图,那将导致失败.

这是一个激发它的示例代码(循环是为了增加并发修改的可能性,否则我们将在goroutine调度程序的手中):

m := map[string]string{"\xff": "a"}

go func() {
    for i := 0; i < 10000; i++ {
        m["x"] = "b"
    }
}()

for i := 0; i < 10000; i++ {
    if _, err := json.Marshal(m); err != nil {
        panic(err)
    }
}
Run Code Online (Sandbox Code Playgroud)

另请注意,在这种情况下json.Marshal()也不会返回(就像内存不足错误的情况一样),而是运行时会故意使应用程序崩溃.输出将是:

fatal error: concurrent map iteration and map write
Run Code Online (Sandbox Code Playgroud)