我想从一个包含另一个结构的平面JSON中创建一个嵌套结构:
type Todo struct {
Todo_id int `json:"todo_id" db:"todo_id"`
Todo_name string `json:"todo_name" db:"todo_name"`
User_id int `json:"user_id" db:"user_id"`
Subs []Sub `json:"subs" db:"subs"`
Times Parsed_Time `json:"times" db:"times"`
}
当我反编组JSON时,我收到一个"丢失的目的地名称截止日期"。错误,因为Deadline在Parsed_Time结构体中。是否有一种自定义解组JSON的方法,使JSON的部分将被省略而不会出现错误?我想单独创建一个带有空Times的Todo结构,然后再次运行Unmarshal以将截止日期和其余时间戳分别提取到另一个结构中。这是为了避免向数据库发出两个单独的Get请求。
是的-你可能已经知道,如果一个类型实现了json.Unmarshaler
,它将在json.Unmarshal()
被调用时使用该类型作为第二个参数。经常出现的问题是需要将接收者的类型作为自定义解组代码的一部分解组。有几种方法可以克服这个问题,其中最常见的是使用本地类型进行数据编组。但是,通过明智地使用类型别名,可以节省大量重复的代码。
我已经更新了上面的代码,将Todo
的字段所代表的类型存根,如下所示:
type Sub int
type ParsedTime struct {
Deadline time.Time
Created time.Time
}
type Todo struct {
ID int `json:"todo_id" db:"todo_id"`
Name string `json:"todo_name" db:"todo_name"`
UserID int `json:"user_id" db:"user_id"`
Subs []Sub `json:"subs" db:"subs"`
Times ParsedTime `json:"-" db:"times"`
}
注意,唯一的相关性变化是忽略Times
字段时' json。叫元帅。我只改变了字段名,让我的IDE的过滤器关闭!使用这些类型,我们可以定义自定义解组器,如下所示:
func (t *Todo) UnmarshalJSON(data []byte) error {
type TodoJSON Todo
todo := struct {
*TodoJSON
Deadline string `json:"deadline"`
}{
TodoJSON: (*TodoJSON)(t),
}
if err := json.Unmarshal(data, &todo); err != nil {
return err
}
deadline, err := time.Parse(time.RFC3339, todo.Deadline)
if err != nil {
return err
}
t.Times.Deadline = deadline
return nil
}
在这段代码中使用了两个关键技术。首先,使用类型别名消除了直接使用Todo
时可能出现的无限递归。其次,创建嵌入*Todo
的本地类型消除了完全重新键入Todo
类型字段的需要——只需要添加所需的Deadline
字段。我还假设Deadline
是time.Time
,以表明该代码还允许在分配(time.Parse()
)之前处理该字段。