自定义将结构解组为切片映射

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
}

https://go.dev/play/p/Lj8a8Gx9fWH