说我有以下代码:
m := map[string]string{}
//... do stuff to the map
b, err := json.Marshal(m)
在这种情况下,json.Marshal
调用是否会返回错误?
我想知道部分是出于好奇,部分是考虑我是否需要担心错误检查。
答案 0 :(得分:5)
由于任何有效的string
值都是有效的密钥,而且是JSON中的有效值(有关详细信息,请参阅Which characters are valid/invalid in a JSON key name?),理论上它不会返回任何错误。
如果发生内存不足错误,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)
输出(在Go Playground上尝试):
{"\ufffd":"a"} <nil>
此行为记录在json.Marshal()
:
字符串值编码为强制转换为有效UTF-8的JSON字符串,用Unicode替换符号替换无效字节。
编组map[string]string
可能永远不会返回错误,但是,您应该始终检查返回的错误,除非文档明确指出返回的error
始终是nil
( json.Marshal()
的doc没有记录这种行为)。这个罕见的例子是rand.Read()
,它记录“它总是返回len(p)和nil错误”。
标准库也有可能存在错误,因此即使json
包的实现可能打算在编组{{1}时也不会返回任何错误},一个错误可能会导致它仍然返回非map[string]string
错误。
另请参阅相关问题:Go : When will json.Unmarshal to struct return error?
为了完整起见,让我们讨论在nil
传递给json.Marshal()
时可能导致map[string]string
失败的另一个问题。
Go 1.6 added轻量级并发滥用地图检测到运行时,您可以在此处阅读更多相关信息:How to recover from concurrent map writes?
这意味着Go运行时可以检测是否在goroutine中读取或修改了地图,并且它也被另一个goroutine同时修改,没有同步。
因此,此处的方案是我们将map[string]string
传递给json.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)
}
}
另请注意,在这种情况下,json.Marshal()
也不会返回(就像内存不足错误的情况一样),而是运行时会故意使应用程序崩溃。输出将是:
fatal error: concurrent map iteration and map write