自定义将结构体反序列化为切片映射。

huangapple go评论83阅读模式
英文:

Custom unmarshaling a struct into a map of slices

问题

我以为我现在已经理解了解组合,但看来并不是这样。我在使用Go语言进行解组合时遇到了一些困难。以下是我目前的代码:

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字段将成功解组合,但由于某种原因,map没有任何值。需要帮助吗?

英文:

I thought I understood unmarshalling by now, but I guess not. I'm having a little bit of trouble unmarshalling a map in go. Here is the code that I have so far

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)
}

after running the code, the last field will unmarshal fine, but for whatever reason, the map is left without any value. Any help?

答案1

得分: 5

对于特定的示例JSON,解决方案是不使用map,而是改变OHLC_RESS的结构,使其与JSON的结构匹配,即:

type OHLC_RESS struct {
    Pair []Candles `json:"XXBTZUSD"`
    Last int64     `json:"last"`
}

然而,我认为你选择使用map的原因是JSON对象中保存“pairs”的键可能会变化,因此无法将它们硬编码到字段的标签中。

要理解为什么你的代码不能产生期望的结果,你必须意识到两件事。首先,结构体字段的顺序对JSON对象的键的解码方式没有影响。其次,名称Pair对于解码器来说没有特殊含义。因此,默认情况下,解码器无法知道你希望将"XXBTZUSD": [...]元素解码到Pair映射中。

因此,为了获得期望的结果,你可以让OHLC_RESS实现json.Unmarshaler接口,并执行以下操作:

func (r *OHLC_RESS) UnmarshalJSON(d []byte) error {
    // 首先,仅解码对象的键,并将值保留为原始的、未解码的JSON
    var obj map[string]json.RawMessage
    if err := json.Unmarshal(d, &obj); err != nil {
        return err
    }

    // 接下来,查找“last”元素的原始、未解码的值,
    // 如果存在,则将其解码到Last字段中
    if last, ok := obj["last"]; ok {
        if err := json.Unmarshal(last, &r.Last); err != nil {
            return err
        }

        // 删除该元素,以便在下面解码其余部分时不会干扰
        delete(obj, "last")
    }

    // 最后,解码对象中的其余元素值,并将它们存储在Pair字段中
    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

英文:

The expedient solution, for the specific example JSON, would be to NOT use a map at all but instead change the structure of OHLC_RESS so that it matches the structure of the JSON, i.e.

type OHLC_RESS struct {
    Pair []Candles `json:"XXBTZUSD"`
    Last int64     `json:"last"`
}

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


However it's safe to assume, I think, that the reason you've opted to use a map is because the JSON object's key(s) that hold the "pairs" can vary and so hardcoding them into the field's tag is out of the question.

To understand why your code doesn't produce the desired result, you have to realize two things. First, the order of a struct's fields has no bearing on how the keys of a JSON object will be decoded. Second, the name Pair holds no special meaning for the unmarshaler. Therefore, by default, the unmarshaler has no way of knowing that your wish is to decode the "XXBTZUSD": [ ... ] element into the Pair map.

So, to get your desired result, you can have the OHLC_RESS implement the json.Unmarshaler interface and do the following:

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

huangapple
  • 本文由 发表于 2022年5月20日 12:48:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/72313698.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定