duanmo5724 2018-12-31 04:52
浏览 69
已采纳

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

I am writing a simple Go program to consume a simple API. Some values are not correctly unmarshaling into my struct, and I have traced the issue to the invalid key names in the returned JSON object.

I can reproduce the issue with this code:

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
", res.Valid)
fmt.Printf("Invalid: %s
", res.Invalid)

The resulting output:

Valid:   I'm Valid
Invalid: 

My expected output:

Valid:   I'm Valid
Invalid: I should be valid, but I'm not

I've tried options such as escaping the ' in the struct tag, but this either results in an error or it is just ignored. I've also looked into alternate methods but have come back empty-handed.

How would this issue be properly handled on my end? Would it be better to strip the ' before unmarshaling? Or is there some other way that I could accept the single quote?

  • 写回答

1条回答 默认 最新

  • duan246558 2018-12-31 05:16
    关注

    According to the docs for json.Marshal...

    The key name will be used if it's a non-empty string consisting of only Unicode letters, digits, and ASCII punctuation except quotation marks, backslash, and comma.

    The relevant code appears to be isValidTag. According to the comment, "quote chars are reserved" for future use in the tag syntax.

    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
    }
    

    You can work around this by using an interface instead of a struct.

    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")
            }
        }
    }
    

    See "Decoding arbitrary data" in JSON and Go for more.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 求差集那个函数有问题,有无佬可以解决
  • ¥15 【提问】基于Invest的水源涵养
  • ¥20 微信网友居然可以通过vx号找到我绑的手机号
  • ¥15 寻一个支付宝扫码远程授权登录的软件助手app
  • ¥15 解riccati方程组
  • ¥15 display:none;样式在嵌套结构中的已设置了display样式的元素上不起作用?
  • ¥15 使用rabbitMQ 消息队列作为url源进行多线程爬取时,总有几个url没有处理的问题。
  • ¥15 Ubuntu在安装序列比对软件STAR时出现报错如何解决
  • ¥50 树莓派安卓APK系统签名
  • ¥65 汇编语言除法溢出问题