当在文件中将“+”转义为“\u002b”时,从 JSON 解组“time.Time”失败,但在纯字符串中工作:无法将“\\u002b00:00\””解析为“Z07:00”

kon*_*tin 7 json go unmarshalling

我正在解组到一个具有time.Time名为 Foo 的字段的结构:

type AStructWithTime struct {
    Foo time.Time `json:"foo"`
}
Run Code Online (Sandbox Code Playgroud)

我的期望是,解组后我会得到这样的结果:

var expectedStruct = AStructWithTime{
    Foo: time.Date(2022, 9, 26, 21, 0, 0, 0, time.UTC),
}
Run Code Online (Sandbox Code Playgroud)

工作示例 1:将纯 JSON 对象转换为结构

当使用纯 json 字符串时,这工作得很好:

func Test_Unmarshalling_DateTime_From_String(t *testing.T) {
    jsonStrings := []string{
        "{\"foo\": \"2022-09-26T21:00:00Z\"}",           // trailing Z = UTC offset
        "{\"foo\": \"2022-09-26T21:00:00+00:00\"}",      // explicit zero offset
        "{\"foo\": \"2022-09-26T21:00:00\u002b00:00\"}", // \u002b is an escaped '+'
    }
    for _, jsonString := range jsonStrings {
        var deserializedStruct AStructWithTime
        err := json.Unmarshal([]byte(jsonString), &deserializedStruct)
        if err != nil {
            t.Fatalf("Could not unmarshal '%s': %v", jsonString, err) // doesn't happen
        }
        if deserializedStruct.Foo.Unix() != expectedStruct.Foo.Unix() {
            t.Fatal("Unmarshalling is erroneous") // doesn't happen
        }
        // works; no errors
    }
}
Run Code Online (Sandbox Code Playgroud)

工作示例 2:将 JSON 数组转换为切片

如果我将 json 数组中的相同对象解组到切片中,它也可以工作:

func Test_Unmarshalling_DateTime_From_Array(t *testing.T) {
    // these are just the same objects as above, just all in one array instead of as single objects/dicts
    jsonArrayString := "[{\"foo\": \"2022-09-26T21:00:00Z\"},{\"foo\": \"2022-09-26T21:00:00+00:00\"},{\"foo\": \"2022-09-26T21:00:00\u002b00:00\"}]"
    var slice []AStructWithTime // and now I need to unmarshal into a slice
    unmarshalErr := json.Unmarshal([]byte(jsonArrayString), &slice)
    if unmarshalErr != nil {
        t.Fatalf("Could not unmarshal array: %v", unmarshalErr)
    }
    for index, instance := range slice {
        if instance.Foo.Unix() != expectedStruct.Foo.Unix() {
            t.Fatalf("Unmarshalling failed for index %v: Expected %v but got %v", index, expectedStruct.Foo, instance.Foo)
        }
    }
    // works; no errors
}
Run Code Online (Sandbox Code Playgroud)

工作的例子

现在,我使用从文件“test.json”读取的 JSON 进行相同的解组。它的内容是上面工作示例中的数组:

[
  {
    "foo": "2022-09-26T21:00:00Z"
  },
  {
    "foo": "2022-09-26T21:00:00+00:00"
  },
  {
    "foo": "2022-09-26T21:00:00\u002b00:00"
  }
]
Run Code Online (Sandbox Code Playgroud)

代码是:

func Test_Unmarshalling_DateTime_From_File(t *testing.T) {
    fileName := "test.json"
    fileContent, readErr := os.ReadFile(filepath.FromSlash(fileName))
    if readErr != nil {
        t.Fatalf("Could not read file %s: %v", fileName, readErr)
    }
    if fileContent == nil {
        t.Fatalf("File %s must not be empty", fileName)
    }
    var slice []AStructWithTime
    unmarshalErr := json.Unmarshal(fileContent, &slice)
    if unmarshalErr != nil {
        // ERROR HAPPENS HERE
        // Could not unmarshal file content test.json: parsing time "\"2022-09-26T21:00:00\\u002b00:00\"" as "\"2006-01-02T15:04:05Z07:00\"": cannot parse "\\u002b00:00\"" as "Z07:00"
        t.Fatalf("Could not unmarshal file content %s: %v", fileName, unmarshalErr)
    }
    for index, instance := range slice {
        if instance.Foo.Unix() != expectedStruct.Foo.Unix() {
            t.Fatalf("Unmarshalling failed for index %v in file %s. Expected %v but got %v", index, fileName, expectedStruct.Foo, instance.Foo)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

由于转义了“+”,它失败了。

将时间“2022-09-26T21:00:00\u002b00:00”解析为“2006-01-02T15:04:05Z07:00”:无法将“\u002b00:00”解析为“Z07:00” ”

问题:为什么从文件读取 time.Time 字段时解组会失败,但从相同字符串读取相同 json 时解组却可以?

Nic*_*rey 5

我相信这是一个错误encoding/json

\n

https://www.json.org上的 JSON 语法和RFC 8259 第 7 节:字符串中的 IETF JSON 定义都规定 JSON 字符串可以包含 Unicode 转义序列:

\n
\n

7. 弦乐

\n

字符串的表示形式类似于 C\n 系列编程语言中使用的约定。字符串以引号\n开头和结尾。所有 Unicode 字符都可以放在引号内,\n除了必须转义的字符:引号、反向\n斜线和控制字符(U+0000 到 U+001F)。

\n

任何字符都可以被转义。如果字符位于基本\n多语言平面(U+0000 到 U+FFFF)中,则它可以表示为\n六个字符序列:反斜线,后跟小写字母\nu,后跟四个十六进制数字对字符的代码点进行编码。十六进制字母 A 到 F 可以是大写或小写。\n因此,例如,仅包含单个反斜线\n字符的字符串可以表示为“\\u005C”。

\n

。。。

\n

为了转义不在基本多语言平面中的扩展字符,该字符被表示为 12 个字符的序列,并对 UTF-16 代理项对进行编码。因此,例如,仅包含 G 谱号字符 (U+1D11E) 的字符串可以表示为“\\uD834\\uDD1E”。

\n
\nstring = quotation-mark *char quotation-mark\n\nchar = unescaped /\n       escape (\n          %x22 /          ; "    quotation mark  U+0022\n          %x5C /          ; \\    reverse solidus U+005C\n          %x2F /          ; /    solidus         U+002F\n          %x62 /          ; b    backspace       U+0008\n          %x66 /          ; f    form feed       U+000C\n          %x6E /          ; n    line feed       U+000A\n          %x72 /          ; r    carriage return U+000D\n          %x74 /          ; t    tab             U+0009\n          %x75 4HEXDIG )  ; uXXXX                U+XXXX\n\nescape = %x5C              ; \\\n\nquotation-mark = %x22      ; "\n\nunescaped = %x20-21 / %x23-5B / %x5D-10FFFF\n\n
Run Code Online (Sandbox Code Playgroud)\n
\n

原始帖子中的 JSON 文档

\n
{\n  "foo": "2022-09-26T21:00:00\\u002b00:00"\n}   \n
Run Code Online (Sandbox Code Playgroud)\n

使用 Node.js 在 Node.js 中完美解析和反序列化JSON.parse()

\n

这是演示该错误的示例:

\n
package main\n\nimport (\n    "encoding/json"\n    "fmt"\n    "time"\n)\n\nvar document []byte = []byte(`\n{\n  "value": "2022-09-26T21:00:00\\u002b00:00"\n}\n`)\n\nfunc main() {\n\n    deserializeJsonAsTime()\n\n    deserializeJsonAsString()\n\n}\n\nfunc deserializeJsonAsTime() {\n    fmt.Println("")\n    fmt.Println("Deserializing JSON as time.Time ...")\n\n    type Widget struct {\n        Value time.Time `json: "value"`\n    }\n\n    expected := Widget{\n        Value: time.Date(2022, 9, 26, 21, 0, 0, 0, time.UTC),\n    }\n    actual := Widget{}\n    err := json.Unmarshal(document, &actual)\n\n    switch {\n    case err != nil:\n        fmt.Println("Error deserializing JSON as time.Time")\n        fmt.Println(err)\n    case actual.Value != expected.Value:\n        fmt.Printf("Unmarshalling failed: expected %v but got %v\\n", expected.Value, actual.Value)\n    default:\n        fmt.Println("Sucess")\n    }\n\n}\n\nfunc deserializeJsonAsString() {\n    fmt.Println("")\n    fmt.Println("Deserializing JSON as string ...")\n\n    type Widget struct {\n        Value string `json: "value"`\n    }\n\n    expected := Widget{\n        Value: "2022-09-26T21:00:00+00:00",\n    }\n    actual := Widget{}\n    err := json.Unmarshal(document, &actual)\n\n    switch {\n    case err != nil:\n        fmt.Println("Error deserializing JSON as string")\n        fmt.Println(err)\n    case actual.Value != expected.Value:\n        fmt.Printf("Unmarshalling failed: expected %v but got %v\\n", expected.Value, actual.Value)\n    default:\n        fmt.Println("Sucess")\n    }\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n

当运行 \xe2\x80\x94 时,请参阅https://goplay.tools/snippet/fHQQVJ8GfPp \xe2\x80\x94 我们得到:

\n
package main\n\nimport (\n    "encoding/json"\n    "fmt"\n    "time"\n)\n\nvar document []byte = []byte(`\n{\n  "value": "2022-09-26T21:00:00\\u002b00:00"\n}\n`)\n\nfunc main() {\n\n    deserializeJsonAsTime()\n\n    deserializeJsonAsString()\n\n}\n\nfunc deserializeJsonAsTime() {\n    fmt.Println("")\n    fmt.Println("Deserializing JSON as time.Time ...")\n\n    type Widget struct {\n        Value time.Time `json: "value"`\n    }\n\n    expected := Widget{\n        Value: time.Date(2022, 9, 26, 21, 0, 0, 0, time.UTC),\n    }\n    actual := Widget{}\n    err := json.Unmarshal(document, &actual)\n\n    switch {\n    case err != nil:\n        fmt.Println("Error deserializing JSON as time.Time")\n        fmt.Println(err)\n    case actual.Value != expected.Value:\n        fmt.Printf("Unmarshalling failed: expected %v but got %v\\n", expected.Value, actual.Value)\n    default:\n        fmt.Println("Sucess")\n    }\n\n}\n\nfunc deserializeJsonAsString() {\n    fmt.Println("")\n    fmt.Println("Deserializing JSON as string ...")\n\n    type Widget struct {\n        Value string `json: "value"`\n    }\n\n    expected := Widget{\n        Value: "2022-09-26T21:00:00+00:00",\n    }\n    actual := Widget{}\n    err := json.Unmarshal(document, &actual)\n\n    switch {\n    case err != nil:\n        fmt.Println("Error deserializing JSON as string")\n        fmt.Println(err)\n    case actual.Value != expected.Value:\n        fmt.Printf("Unmarshalling failed: expected %v but got %v\\n", expected.Value, actual.Value)\n    default:\n        fmt.Println("Sucess")\n    }\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n

由于反序列化包含 Unicode 转义序列的 JSON 字符串会产生string正确/预期的结果 \xe2\x80\x94 转义序列被转换为预期的符文/字节序列 \xe2\x80\x94 问题似乎在于处理代码反序列化为time.Time(它似乎没有反序列化为字符串,然后将字符串值解析为time.Time.

\n

  • 有趣 - 看起来有一个[现有问题](https://github.com/golang/go/issues/47353)涵盖了这一点。 (4认同)