【问题标题】:Unmarshalling a JSON that may or may not return an array?解组可能返回或不返回数组的 JSON?
【发布时间】:2015-11-27 13:06:45
【问题描述】:

我正在从第三方网站(家庭用电量)检索 JSON,根据我从该网站请求的内容,返回的 JSON 可能是数组,也可能不是数组。例如,如果我请求我的智能电表列表,我会得到这个(结果被截断,由于大尺寸):

{"gwrcmds":{"gwrcmd":{"gcmd":"SPA_UserGetSmartMeterList","gdata":{"gip":{"version":"1"...

其中 gwrcmd 是单个元素。

但如果我要求最后半小时的用电量,我会得到:

{"gwrcmds":{"gwrcmd":[{"gcmd":"DeviceGetChart","gdata":{"gip":{"version":"1" ...

看看 gwrcmd 现在是一个数组吗?

在我的 Go 应用程序中,我有一个看起来像这样的结构(再次被截断,因为它会持续一段时间。“版本”下面还有更多子结构和属性:

type Response struct {
    Gwrcmds struct {
        Gwrcmd struct {
            Gcmd  string
            Gdata struct {
                Gip struct {
                    Version string

如果gwrcmd 是一个数组,Gwrcmd 需要是一个[]struct { },但如果不是,它只是一个普通的旧struct { }

问题是json.Unmarshal 只是在 JSON 有一个数组而结构没有切片时返回一个错误(反之亦然)。

我是否需要创建第二个结构来复制第一个结构([]struct { } 除外),或者有更好的方法吗?我想到了一些与接口有关的东西,但我还没有真正接触过这些,所以我不是 100% 确定它们。

【问题讨论】:

  • 一个突然出现在我脑海中但似乎不太理想的想法是实现一个io.Reader,它在单个gcmd结构之前添加[,并在其末尾添加]然后从那个读者那里解组。
  • 除了现有的答案...您提出请求类型 A 并获得响应类型 RA,您提出请求类型 B 并获得响应类型 RB...也许您不应该尝试阅读两种不同的东西(RA和 RB) 使用完全相同的代码。它可以像type ResponseOne struct { Gwrcmdc struct { Gwrcmd GwCmdType } }type ResponseMany struct { Gwrcmds struct { Gwrcmd []GwCmdType } } 一样简单。

标签: json go


【解决方案1】:

您可以尝试制作自定义 json 解组方法,例如

func (a *GwrcmCustom) UnmarshalJSON(b []byte) (err error) {
    g, ga := Gwrcmd{}, []Gwrcmd{}
    if err = json.Unmarshal(b, &g); err == nil {
        *a = make([]Gwrcmd, 1)
        []Gwrcmd(*a)[0] = Gwrcmd(g)
        return
    }
    if err = json.Unmarshal(b, &ga); err == nil {
        *a = GwrcmCustom(ga)
        return
    }
    return
}

GwrcmCustom 是一个自定义类型,Gwrcm 的切片

type GwrcmCustom []Gwrcmd

所以我们总是会得到切片

Go playground

上试试这个

我希望这会有所帮助

【讨论】:

    【解决方案2】:

    通常,每当您有一个未知类型的 JSON 值时,您将使用json.RawMessage 获取它,查看它,并将其正确解组为相应的类型。一个简化的例子:

    // The A here can be either an object, or a JSON array of objects.
    type Response struct {
        RawAWrapper struct {
            RawA json.RawMessage `json:"a"`
        }
        A  A   `json:"-"`
        As []A `json:"-"`
    }
    
    type A struct {
        B string
    }
    
    func (r *Response) UnmarshalJSON(b []byte) error {
        if err := json.Unmarshal(b, &r.RawAWrapper); err != nil {
            return err
        }
        if r.RawAWrapper.RawA[0] == '[' {
            return json.Unmarshal(r.RawAWrapper.RawA, &r.As)
        }
        return json.Unmarshal(r.RawAWrapper.RawA, &r.A)
    }
    

    游乐场:http://play.golang.org/p/2d_OrGltDu

    不过,根据第一个字节猜测内容对我来说似乎不太可靠。通常,您的 JSON 中会有某种线索(例如与动态字段处于同一级别的 lengthtype 字段),它会告诉您是否有对象或数组。

    另见:

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-07
    • 2015-01-21
    • 2016-04-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多