go json 解组选项
go json unmarshal options
试图找到一个简单的解决方案 marshaling/unmashaling 到以下结构
type Resource struct {
Data []ResourceData `json:"data"`
}
type ResourceData struct {
Id string `json:"id"`
Type string `json:"type"`
Attributes map[string]interface{} `json:"attributes"`
Relationships map[string]Resource `json:"relationships"`
}
r := Resource{}
json.Unmarshal(body, &r)
这很棒,如果:
body = `{"data":[{"id":"1","type":"blah"}]}`
但是我还需要它来回复:
body = `{"data":{"id":"1","type":"blah"}}` //notice no slice
我可以做一个单独的类型
type ResourceSingle struct {
Data ResourceData `json:"data"`
}
但是,这意味着需要复制我附加到资源的所有功能,这是可能的。但是,我需要在执行它之前找出要解组的类型,再加上关系部分,每个都可能包含数据:[]{} 或数据{},所以这个想法不会工作。
或者我可以使用
map[string]*json.RawMessage
//or
type Resource struct {
Data *json.RawMessage `json:"data"`
}
但是,当处于 json 形式时,我如何知道它是一个切片还是一个节点来提供正确的结构来解组?
我最后的办法是将 umarshal 映射到[string]接口并使用大量反射测试..但这是非常冗长的。
想法?
亲切的问候,jJ
有多种构造它的方法,但最简单的技术归结为实现 json.Unmarshaler
并检查数据类型。您可以最低限度地解析 json 字节,通常只是第一个字符,或者您可以尝试解组为每种类型和 return 成功的类型。
我们将在这里使用后一种技术,无论传入数据的格式如何,都将 ResourceData 插入到切片中,因此我们始终可以以相同的方式对其进行操作:
type Resource struct {
Data []ResourceData
}
func (r *Resource) UnmarshalJSON(b []byte) error {
// this gives us a temporary location to unmarshal into
m := struct {
DataSlice struct {
Data []ResourceData `json:"data"`
}
DataStruct struct {
Data ResourceData `json:"data"`
}
}{}
// try to unmarshal the data with a slice
err := json.Unmarshal(b, &m.DataSlice)
if err == nil {
log.Println("got slice")
r.Data = m.DataSlice.Data
return nil
} else if err, ok := err.(*json.UnmarshalTypeError); !ok {
// something besides a type error occurred
return err
}
// try to unmarshal the data with a struct
err = json.Unmarshal(b, &m.DataStruct)
if err != nil {
return err
}
log.Println("got struct")
r.Data = append(r.Data, m.DataStruct.Data)
return nil
}
试图找到一个简单的解决方案 marshaling/unmashaling 到以下结构
type Resource struct {
Data []ResourceData `json:"data"`
}
type ResourceData struct {
Id string `json:"id"`
Type string `json:"type"`
Attributes map[string]interface{} `json:"attributes"`
Relationships map[string]Resource `json:"relationships"`
}
r := Resource{}
json.Unmarshal(body, &r)
这很棒,如果:
body = `{"data":[{"id":"1","type":"blah"}]}`
但是我还需要它来回复:
body = `{"data":{"id":"1","type":"blah"}}` //notice no slice
我可以做一个单独的类型
type ResourceSingle struct {
Data ResourceData `json:"data"`
}
但是,这意味着需要复制我附加到资源的所有功能,这是可能的。但是,我需要在执行它之前找出要解组的类型,再加上关系部分,每个都可能包含数据:[]{} 或数据{},所以这个想法不会工作。
或者我可以使用
map[string]*json.RawMessage
//or
type Resource struct {
Data *json.RawMessage `json:"data"`
}
但是,当处于 json 形式时,我如何知道它是一个切片还是一个节点来提供正确的结构来解组?
我最后的办法是将 umarshal 映射到[string]接口并使用大量反射测试..但这是非常冗长的。
想法?
亲切的问候,jJ
有多种构造它的方法,但最简单的技术归结为实现 json.Unmarshaler
并检查数据类型。您可以最低限度地解析 json 字节,通常只是第一个字符,或者您可以尝试解组为每种类型和 return 成功的类型。
我们将在这里使用后一种技术,无论传入数据的格式如何,都将 ResourceData 插入到切片中,因此我们始终可以以相同的方式对其进行操作:
type Resource struct {
Data []ResourceData
}
func (r *Resource) UnmarshalJSON(b []byte) error {
// this gives us a temporary location to unmarshal into
m := struct {
DataSlice struct {
Data []ResourceData `json:"data"`
}
DataStruct struct {
Data ResourceData `json:"data"`
}
}{}
// try to unmarshal the data with a slice
err := json.Unmarshal(b, &m.DataSlice)
if err == nil {
log.Println("got slice")
r.Data = m.DataSlice.Data
return nil
} else if err, ok := err.(*json.UnmarshalTypeError); !ok {
// something besides a type error occurred
return err
}
// try to unmarshal the data with a struct
err = json.Unmarshal(b, &m.DataStruct)
if err != nil {
return err
}
log.Println("got struct")
r.Data = append(r.Data, m.DataStruct.Data)
return nil
}