一尘不染

可以将map [string] string编组到json会返回错误吗?

go

说我有以下代码:

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

在这种情况下,有什么办法可以使json.Marshal调用返回错误?

我想知道,部分原因是出于好奇,另一部分是考虑是否需要担心该错误检查。


阅读 209

收藏
2020-07-02

共1个答案

一尘不染

由于任何有效值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)

输出(在Go Playground上尝试):

{"\ufffd":"a"} <nil>

此行为记录在json.Marshal()

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

封送a map[string]string可能永远不会返回错误,但是,除非文档明确指出返回error的总是nil(除非docjson.Marshal()并未记录这种行为),否则应始终检查返回的错误。这种罕见的例子是rand.Read()哪个文档_“总是返回len(p)和nil错误”_ 。

而且标准库也有可能存在错误,因此,即使json封包的实现可能在封送a时不 打算
返回任何错误map[string]string,但错误可能会导致它仍然返回非nil错误。

并发地图修改

为了完整起见,让我们讨论json.Marshal()将a 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)
    }
}

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

fatal error: concurrent map iteration and map write
2020-07-02