自定义将结构解组为切片映射
Custom unmarshaling a struct into a map of slices
我以为我现在已经理解解编组了,但我想不是。我在解组地图时遇到了一些麻烦。这是我到目前为止的代码
type OHLC_RESS struct {
Pair map[string][]Candles
Last int64 `json:"last"`
}
type Candles struct {
Time uint64
Open string
High string
Low string
Close string
VWAP string
Volume string
Count int
}
func (c *Candles) UnmarshalJSON(d []byte) error {
tmp := []interface{}{&c.Time, &c.Open, &c.High, &c.Low, &c.Close, &c.VWAP, &c.Volume, &c.Count}
length := len(tmp)
err := json.Unmarshal(d, &tmp)
if err != nil {
return err
}
g := len(tmp)
if g != length {
return fmt.Errorf("Lengths don't match: %d != %d", g, length)
}
return nil
}
func main() {
response := []byte(`{"XXBTZUSD":[[1616662740,"52591.9","52599.9","52591.8","52599.9","52599.1","0.11091626",5],[1616662740,"52591.9","52599.9","52591.8","52599.9","52599.1","0.11091626",5]],"last":15}`)
var resp OHLC_RESS
err := json.Unmarshal(response, &resp)
fmt.Println("resp: ", resp)
}
在 运行 代码之后,last
字段将很好地解组,但无论出于何种原因,地图都没有任何价值。有帮助吗?
对于特定示例 JSON,权宜之计的解决方案是根本不使用映射,而是更改 OHLC_RESS
的结构,使其与 [=41] 的结构相匹配=],即
type OHLC_RESS struct {
Pair []Candles `json:"XXBTZUSD"`
Last int64 `json:"last"`
}
https://go.dev/play/p/Z9PhJt3wX33
然而,我认为可以安全地假设,您选择使用地图的原因是因为 JSON 对象的保存“对”的键可能会有所不同,因此对它们进行硬编码进入字段的标签是不可能的。
要理解为什么您的代码没有产生预期的结果,您必须了解两件事。首先,结构字段的 order 与 JSON 对象的键如何解码没有关系。其次,名称 Pair
对于解组器 没有特殊意义。因此,默认情况下,解组器 无法 知道您希望将 "XXBTZUSD": [ ... ]
元素解码到 Pair
映射中。
因此,为了获得您想要的结果,您可以让 OHLC_RESS
实现 json.Unmarshaler
接口并执行以下操作:
func (r *OHLC_RESS) UnmarshalJSON(d []byte) error {
// first, decode just the object's keys and leave
// the values as raw, non-decoded JSON
var obj map[string]json.RawMessage
if err := json.Unmarshal(d, &obj); err != nil {
return err
}
// next, look up the "last" element's raw, non-decoded value
// and, if it is present, then decode it into the Last field
if last, ok := obj["last"]; ok {
if err := json.Unmarshal(last, &r.Last); err != nil {
return err
}
// remove the element so it's not in
// the way when decoding the rest below
delete(obj, "last")
}
// finally, decode the rest of the element values
// in the object and store them in the Pair field
r.Pair = make(map[string][]Candles, len(obj))
for key, val := range obj {
cc := []Candles{}
if err := json.Unmarshal(val, &cc); err != nil {
return err
}
r.Pair[key] = cc
}
return nil
}
我以为我现在已经理解解编组了,但我想不是。我在解组地图时遇到了一些麻烦。这是我到目前为止的代码
type OHLC_RESS struct {
Pair map[string][]Candles
Last int64 `json:"last"`
}
type Candles struct {
Time uint64
Open string
High string
Low string
Close string
VWAP string
Volume string
Count int
}
func (c *Candles) UnmarshalJSON(d []byte) error {
tmp := []interface{}{&c.Time, &c.Open, &c.High, &c.Low, &c.Close, &c.VWAP, &c.Volume, &c.Count}
length := len(tmp)
err := json.Unmarshal(d, &tmp)
if err != nil {
return err
}
g := len(tmp)
if g != length {
return fmt.Errorf("Lengths don't match: %d != %d", g, length)
}
return nil
}
func main() {
response := []byte(`{"XXBTZUSD":[[1616662740,"52591.9","52599.9","52591.8","52599.9","52599.1","0.11091626",5],[1616662740,"52591.9","52599.9","52591.8","52599.9","52599.1","0.11091626",5]],"last":15}`)
var resp OHLC_RESS
err := json.Unmarshal(response, &resp)
fmt.Println("resp: ", resp)
}
在 运行 代码之后,last
字段将很好地解组,但无论出于何种原因,地图都没有任何价值。有帮助吗?
对于特定示例 JSON,权宜之计的解决方案是根本不使用映射,而是更改 OHLC_RESS
的结构,使其与 [=41] 的结构相匹配=],即
type OHLC_RESS struct {
Pair []Candles `json:"XXBTZUSD"`
Last int64 `json:"last"`
}
https://go.dev/play/p/Z9PhJt3wX33
然而,我认为可以安全地假设,您选择使用地图的原因是因为 JSON 对象的保存“对”的键可能会有所不同,因此对它们进行硬编码进入字段的标签是不可能的。
要理解为什么您的代码没有产生预期的结果,您必须了解两件事。首先,结构字段的 order 与 JSON 对象的键如何解码没有关系。其次,名称 Pair
对于解组器 没有特殊意义。因此,默认情况下,解组器 无法 知道您希望将 "XXBTZUSD": [ ... ]
元素解码到 Pair
映射中。
因此,为了获得您想要的结果,您可以让 OHLC_RESS
实现 json.Unmarshaler
接口并执行以下操作:
func (r *OHLC_RESS) UnmarshalJSON(d []byte) error {
// first, decode just the object's keys and leave
// the values as raw, non-decoded JSON
var obj map[string]json.RawMessage
if err := json.Unmarshal(d, &obj); err != nil {
return err
}
// next, look up the "last" element's raw, non-decoded value
// and, if it is present, then decode it into the Last field
if last, ok := obj["last"]; ok {
if err := json.Unmarshal(last, &r.Last); err != nil {
return err
}
// remove the element so it's not in
// the way when decoding the rest below
delete(obj, "last")
}
// finally, decode the rest of the element values
// in the object and store them in the Pair field
r.Pair = make(map[string][]Candles, len(obj))
for key, val := range obj {
cc := []Candles{}
if err := json.Unmarshal(val, &cc); err != nil {
return err
}
r.Pair[key] = cc
}
return nil
}