包含接口列表的struct上的UnmarshalJSON

时间:2017-06-29 13:23:07

标签: json go

我想UnmarshalJSON是一个包含接口的结构,如下所示:

type Filterer interface {
    Filter(s string) error
}

type FieldFilter struct {
    Key string
    Val string
}    

func (ff *FieldFilter) Filter(s string) error {
    // Do something
}

type Test struct {
    Name string
    Filters []Filterer
}

我的想法是像这样发送一个json:

{
    "Name": "testing",
    "Filters": [
        {
            "FieldFilter": {
                "Key": "key",
                "Val": "val"
            }
        }
    ]
}

但是,当将此json发送到unmarshaler时,会返回以下异常:json: cannot unmarshal object into Go struct field Test.Filters of type Filterer

我完全理解这个问题,但不知道如何明智地解决这个问题。在go中寻找解决这个问题的惯用方法的建议。

2 个答案:

答案 0 :(得分:2)

Unmarshal无法知道它应该使用什么类型。唯一可以“制造东西”的情况是,如果要求它解组为interface{},在这种情况下它将使用the rules in the documentation。由于这些类型都不能放入[]Filterer,因此无法解组该字段。如果要解组为struct类型,则必须将该字段指定为该类型。

您可以随时解组为中间结构或地图类型,然后将其自己转换为您想要的任何类型。

答案 1 :(得分:2)

根据我自己的问题,我研究了如何为接口列表实现UnmarshalJSON。最终,这导致我发布了一篇关于如何正确执行此操作的博客文章。基本上有两个主要的解决方案:

  1. 将所需的JSON字符串解析为map[string]*json.RawMessage并从那里开始工作。
  2. 为接口列表创建别名,并为该别名实现UnmarshalJSON。但是,您仍然需要使用map[string]*json.RawMessage和一些手动工作。什么都没有价格!
  3. 我强烈建议采用秒针方法。虽然这两种解决方案可能会产生相同数量的代码行,但利用类型别名并减少对json.RawMessage类型的依赖将使代码更易于管理,尤其是当需要支持多个接口时UnmarshalJSON实现

    要直接回答问题,请先为接口列表创建类型别名:

    type Filterers []Filterer
    

    现在继续实现JSON的解码:

    func (f *Filterers) UnmarshalJSON(b []byte) error {
        var FilterFields map[string]*json.RawMessage
        if err := json.Unmarshal(b, &FilterFields); err != nil {
            return err
        }
        for LFKey, LFValue := range FilterFields {
            if LFKey == "FieldFilter" {
                var MyFieldFilters []*json.RawMessage
                if err := json.Unmarshal(*LFValue, &MyFieldFilters); err != nil {
                    return err
                }
                for _, MyFieldFilter := range MyFieldFilters {
                    var filter FieldFilter
                    if err := json.Unmarshal(*MyFieldFilter, &filter); err != nil {
                        return err
                    }
                    *f = append(*f, &filter)
                }
            }
        }
        return nil
    }
    

    第二种方法的详细解释(包含一些示例和完整的工作代码片段)on my own blog