说我有以下代码:
m := map[string]string{} //... do stuff to the map b, err := json.Marshal(m)
在这种情况下,有什么办法可以使json.Marshal调用返回错误?
json.Marshal
我想知道,部分原因是出于好奇,另一部分是考虑是否需要担心该错误检查。
由于任何有效值string都是有效键,而且也是JSON中的有效值(有关详细信息,请参阅JSON键名中的哪些字符有效/无效?,因此从理论上讲,它不会返回任何错误。
string
如果发生内存不足错误,则json.Marshal()不会返回错误,您的应用将终止并显示错误代码。
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替换符文替换无效字节。
封送a map[string]string可能永远不会返回错误,但是,除非文档明确指出返回error的总是nil(除非docjson.Marshal()并未记录这种行为),否则应始终检查返回的错误。这种罕见的例子是rand.Read()哪个文档_“总是返回len(p)和nil错误”_ 。
map[string]string
error
nil
rand.Read()
而且标准库也有可能存在错误,因此,即使json封包的实现可能在封送a时不 打算 返回任何错误map[string]string,但错误可能会导致它仍然返回非nil错误。
json
为了完整起见,让我们讨论json.Marshal()将a map[string]string传递给它时可能导致失败的另一个问题。
Go 1.6向运行时添加了轻量级的并发滥用映射检测功能
这意味着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