对于encoding/json,如何优雅地处理键名称中包含撇号的 API JSON 调用?

Aub*_*gne 3 json go

我正在编写一个简单的 Go 程序来使用一个简单的 API。某些值未正确解组到我的结构中,并且我已将问题追溯到返回的 JSON 对象中的无效键名称。

我可以使用以下代码重现该问题:

jsonStr := `{
    "valid_json": "I'm Valid",
    "invalid'json": "I should be valid, but I'm not"
}`

type result struct {
    Valid   string `json:"valid_json"`
    Invalid string `json:"invalid'json"`
}

var res result
err := json.Unmarshal([]byte(jsonStr), &res)
if err != nil {
    log.Fatal(err)
}

fmt.Printf("Valid:   %s\n", res.Valid)
fmt.Printf("Invalid: %s\n", res.Invalid)
Run Code Online (Sandbox Code Playgroud)

结果输出:

Valid:   I'm Valid
Invalid: 
Run Code Online (Sandbox Code Playgroud)

我的预期输出:

Valid:   I'm Valid
Invalid: I should be valid, but I'm not
Run Code Online (Sandbox Code Playgroud)

我尝试过诸如转义'结构标记之类的选项,但这要么会导致错误,要么会被忽略。我也研究过其他方法,但空手而归。

我这边该如何妥善处理这个问题呢?在解组之前剥离会更好吗'?或者还有其他方法可以接受单引号吗?

Sch*_*ern 5

根据json.Marshal的文档...

如果键名称是仅由 Unicode 字母、数字和 ASCII 标点符号(引号、反斜杠和逗号除外)组成的非空字符串,则将使用该键名称。

相关代码似乎是isValidTag. 根据评论,“保留引号字符”以供将来在标记语法中使用。

func isValidTag(s string) bool {
    if s == "" {
        return false
    }
    for _, c := range s {
        switch {
        case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", c):
            // Backslash and quote chars are reserved, but
            // otherwise any punctuation chars are allowed
            // in a tag name.
        case !unicode.IsLetter(c) && !unicode.IsDigit(c):
            return false
        }
    }
    return true
}
Run Code Online (Sandbox Code Playgroud)

您可以通过使用接口而不是结构来解决这个问题。

package main
import (
    "fmt"
    "encoding/json"
    "log"
)
func main() {
    jsonStr := `{
        "valid_json": "I'm Valid",
        "invalid'json": "I should be valid, but I'm not"
    }`

    var res interface{}
    err := json.Unmarshal([]byte(jsonStr), &res)
    if err != nil {
        log.Fatal(err)
    }

    m := res.(map[string]interface{})
    for k, v := range m {
        switch vv := v.(type) {
        case string:
            fmt.Println(k, "is string", vv)
        case float64:
            fmt.Println(k, "is float64", vv)
        case []interface{}:
            fmt.Println(k, "is an array:")
            for i, u := range vv {
                fmt.Println(i, u)
            }
        default:
            fmt.Println(k, "is of a type I don't know how to handle")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

有关更多信息,请参阅JSON 和 Go中的“解码任意数据” 。