【问题标题】:Unmarshal json string to a struct that have one element of the struct itself将 json 字符串解组到具有结构本身的一个元素的结构
【发布时间】:2018-03-17 01:15:31
【问题描述】:

我是初学者并试图解组以下 json 字符串

[{
    "db": {
        "url": "mongodb://localhost",
        "port": "27000",
        "uname": "",
        "pass": "",
        "authdb": "",
        "replicas": [
            {
                "rs01": {
                    "url":"mongodb://localhost",
                    "port": "27001",
                    "uname": "",
                    "pass": "",
                    "authdb": ""
                }
            },
            {
                "rs02": {
                    "url":"mongodb://localhost",
                    "port": "27002",
                    "uname": "",
                    "pass": "",
                    "authdb": ""
                }
            }
        ]
    }
}]

这是结构

type DBS struct {
    URL      string `json:url`
    Port     string `json:port`
    Uname    string `json:uname`
    Pass     string `json:pass`
    Authdb   string `json:authdb`
    Replicas []DBS   `json:replicas`
}

这里是函数

func loadConfigs() []DBS {
    var config []DBS
    raw, err := ioutil.ReadFile("./config.json")
    if err != nil {
        fmt.Println(err.Error())
        os.Exit(1)
    }

    json.Unmarshal(raw, &config)
    return config
}

函数正在返回

{     []}

【问题讨论】:

  • 你没有检查 unmarshal 返回的错误。

标签: json go struct


【解决方案1】:

您的 JSON 输入不是 DBS 的切片,因为还有另一个 JSON 对象包装器,DBS 的值属于属性 "db"

再深入一点,"replicaps" 是一个 JSON 数组,其中的对象持有不同的键,它们的值可以由 DBS 表示。

因此,要完整描述您的 JSON,您需要某种“动态”类型。例如,地图就是这样一种动态类型。

因此,您的原始 JSON 输入可以完全使用以下类型建模:[]map[string]DBS。这是一片地图,因为您的 JSON 输入包含一个 JSON 数组。而且map key可以建模任意属性名,value是DBS结构体建模的JSON对象。

查看这个完全解析 JSON 输入的示例:

type DBS struct {
    URL      string           `json:"url"`
    Port     string           `json:"port"`
    Uname    string           `json:"uname"`
    Pass     string           `json:"pass"`
    Authdb   string           `json:"authdb"`
    Replicas []map[string]DBS `json:"replicas"`
}

func main() {
    var dbs []map[string]DBS
    if err := json.Unmarshal([]byte(src), &dbs); err != nil {
        panic(err)
    }
    fmt.Printf("%+v", dbs)
}

注意正确的tag syntax(例如json:"url")。

输出(在Go Playground上试试):

[map[db:{URL:mongodb://localhost Port:27000 Uname: Pass: Authdb: Replicas:[map[rs01:{URL:mongodb://localhost Port:27001 Uname: Pass: Authdb: Replicas:[]}] map[rs02:{URL:mongodb://localhost Port:27002 Uname: Pass: Authdb: Replicas:[]}]]}]]

请注意,您可以进一步建模始终为"db" 的第一级,我们可以切换到指针(我在第一个示例中使用了非指针,因此打印结果可读):

type DBReplicated struct {
    DB *DBS `json:"db"`
}

type DBS struct {
    URL      string            `json:"url"`
    Port     string            `json:"port"`
    Uname    string            `json:"uname"`
    Pass     string            `json:"pass"`
    Authdb   string            `json:"authdb"`
    Replicas []map[string]*DBS `json:"replicas"`
}

func main() {
    var dbs []*DBReplicated
    if err := json.Unmarshal([]byte(src), &dbs); err != nil {
        panic(err)
    }

    db := dbs[0].DB
    fmt.Printf("%+v\n", db)
    for _, dbs := range db.Replicas {
        for name, replica := range dbs {
            fmt.Printf("%s: %+v\n", name, replica)
        }
    }
}

输出(在Go Playground上试试):

&{URL:mongodb://localhost Port:27000 Uname: Pass: Authdb: Replicas:[map[rs01:0x10538200] map[rs02:0x10538240]]}
rs01: &{URL:mongodb://localhost Port:27001 Uname: Pass: Authdb: Replicas:[]}
rs02: &{URL:mongodb://localhost Port:27002 Uname: Pass: Authdb: Replicas:[]}

【讨论】:

    【解决方案2】:

    如果由于某种原因您被现有的 DBS 结构卡住了,您也可以编写自己的 json.Unmarshaler 实现。

    (在下面的示例中,我确实稍微更改了结构以跟踪名称/键,但这是可选的,没有必要进行解组工作。)

    type DBS struct {
        name     string
    
        URL      string `json:url`
        Port     string `json:port`
        Uname    string `json:uname`
        Pass     string `json:pass`
        Authdb   string `json:authdb`
        Replicas []DBS  `json:replicas`
    }
    
    func (db *DBS) UnmarshalJSON(data []byte) error {
        raw := map[string]json.RawMessage{}
        if err := json.Unmarshal(data, &raw); err != nil {
            return err
        }
        if len(raw) > 1 {
            return fmt.Errorf("fail")
        }
    
        type _DBS DBS
        _db := (*_DBS)(db)
        for name, v := range raw {
            db.name = name
            return json.Unmarshal(v, _db)
        }
        return nil
    }
    

    https://play.golang.org/p/c288n7holS

    【讨论】:

      猜你喜欢
      • 2017-06-12
      • 2017-03-17
      • 1970-01-01
      • 1970-01-01
      • 2015-10-21
      • 2023-01-07
      • 2021-08-29
      • 1970-01-01
      • 2016-01-05
      相关资源
      最近更新 更多