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

时间:2018-03-12 15:50:03

标签: json dictionary go marshalling

说我有以下代码:

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

在这种情况下,json.Marshal调用是否会返回错误?

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

1 个答案:

答案 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始终是niljson.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