在Golang中解组具有重叠字段的动态JSON数据

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

Unmarshalling Dynamic JSON Data With Overlapping Fields in Golang

问题

抱歉,如果我提出了一个已经回答过的问题,但我似乎找不到任何类似的情况。我有一个接收动态JSON数据的WebSocket客户端,其中包含重叠字段。字段重叠使得对我来说非常困难进行解组。

我已经为接收到的数据类型创建了结构体,但我需要一种在对其进行解组之前检查JSON数据的方法。我希望接口可以充当临时持有者,然后我将能够将接口与我想要解组的特定结构体匹配,但这似乎不可能,或者我只是不知道如何处理。以下是一些我收到的数据类型和相应的结构体的示例,希望这有所帮助。

  1. 响应1{"connectionID":17973829270596587247,"event":"systemStatus","status":"online","version":"1.9.0"}
  2. 响应2{"channelID":328,"channelName":"ohlc-5","event":"subscriptionStatus","pair":"XBT/USD","status":"subscribed","subscription":{"interval":5,"name":"ohlc"}}
  3. 响应3[328,["1649576721.042916","1649577000.000000","42641.50000","42641.50000","42641.50000","42641.50000","42641.50000","0.00335101",2],"ohlc-5","XBT/USD"]
  4. 响应4{"event":"heartbeat"}
  5. 以下是结构体
  6. import (
  7. "time"
  8. "encoding/json"
  9. )
  10. type ConnStatus struct {
  11. ConnectionID uint64 `json:"connectionID"`
  12. Event string `json:"event"`
  13. Status string `json:"status"`
  14. Version string `json:"version"`
  15. }
  16. type HeartBeat struct {
  17. Event string `json:"event"`
  18. }
  19. type OHLCsuccess struct {
  20. ChannelID int `json:"channelID"`
  21. ChannelName string `json:"channelName"`
  22. Event string `json:"event"`
  23. Pair string `json:"pair"`
  24. Status string `json:"status"`
  25. Subscription OHLC `json:"subscription"`
  26. }
  27. type OHLC struct {
  28. Interval int `json:"interval"`
  29. Name string `json:"name"`
  30. }
  31. type OHLCUpdates struct {
  32. ChannelID int
  33. OHLCArray OHLCNewTrade
  34. ChannelName string
  35. Pair string
  36. }
  37. type OHLCNewTrade struct {
  38. StartTime UnixTime
  39. EndTime UnixTime
  40. Open float64
  41. High float64
  42. Low float64
  43. Close float64
  44. VWAP float64
  45. Volume float64
  46. Count int
  47. }
  48. type UnixTime struct {
  49. time.Time
  50. }
  51. func (u *UnixTime) UnmarshalJSON(d []byte) error {
  52. var ts int64
  53. err := json.Unmarshal(d, &ts)
  54. if err != nil {
  55. return err
  56. }
  57. u.Time = time.Unix(ts, 0).UTC()
  58. return nil
  59. }

有关如何处理这个问题的任何想法吗?提前感谢您的帮助!

英文:

Sorry If i'm posting a question that has already been answered, but I can't seem to find any similar situations on here. I have a websocket client that receives dynamic json data with overlapping fields. The fact that the fields overlap has has made Unmarshalling very difficult for me.

I have structs for the data types I receive, but I need a way to check the json data before I unmarshal it to a specific struct. I was hoping that an interface could act as a temporary holder and I would then be able to match the interface to the specific struct I want to unmarshal to, but that doesn't seem possible, or I just don't know how to go about it. Here are a few examples of the data types I'm receiving and structs to go along with it in case that helps.

  1. response 1: {"connectionID":17973829270596587247,"event":"systemStatus","status":"online","version":"1.9.0"}
  2. response 2: {"channelID":328,"channelName":"ohlc-5","event":"subscriptionStatus","pair":"XBT/USD","status":"subscribed","subscription":{"interval":5,"name":"ohlc"}}
  3. response 3: [328,["1649576721.042916","1649577000.000000","42641.50000","42641.50000","42641.50000","42641.50000","42641.50000","0.00335101",2],"ohlc-5","XBT/USD"]
  4. response 4: {"event":"heartbeat"}
  5. structs below
  6. import (
  7. "time"
  8. "encoding/json"
  9. )
  10. type ConnStatus struct {
  11. ConnectionID uint64 `json:"connectionID"`
  12. Event string `json:"event"`
  13. Status string `json:"status"`
  14. Version string `json:"version"`
  15. }
  16. type HeartBeat struct {
  17. Event string `json:"event"`
  18. }
  19. type OHLCsuccess struct {
  20. ChannelID int `json:"channelID"`
  21. ChannelName string `json:"channelName"`
  22. Event string `json:"event"`
  23. Pair string `json:"pair"`
  24. Status string `json:"status"`
  25. Subscription OHLC `json:"subscription"`
  26. }
  27. type OHLC struct {
  28. Interval int `json:"interval"`
  29. Name string `json:"name"`
  30. }
  31. type OHLCUpdates struct {
  32. ChannelID int
  33. OHLCArray OHLCNewTrade
  34. ChannelName string
  35. Pair string
  36. }
  37. type OHLCNewTrade struct {
  38. StartTime UnixTime
  39. EndTime UnixTime
  40. Open float64
  41. High float64
  42. Low float64
  43. Close float64
  44. VWAP float64
  45. Volume float64
  46. Count int
  47. }
  48. type UnixTime struct {
  49. time.Time
  50. }
  51. func (u *UnixTime) UnmarshalJSON(d []byte) error {
  52. var ts int64
  53. err := json.Unmarshal(d, &ts)
  54. if err != nil {
  55. return err
  56. }
  57. u.Time = time.Unix(ts, 0).UTC()
  58. return nil
  59. }

Any idea(s) on how to go about this? Thanks in advance for the help!

答案1

得分: 1

你是否控制不同的回复?如果是这样,那么在顶层添加一个"type"字段如何?请参考https://eagain.net/articles/go-dynamic-json/上的"How to put everything at the top level"部分获取更多信息。

例如(未经测试):

  1. func UnmarshalJSON(d []byte) error {
  2. var jsonValue map[string]interface{}
  3. err := json.Unmarshal(d, &jsonValue)
  4. if err != nil {
  5. return err
  6. }
  7. switch jsonValue["type"] {
  8. case 1:
  9. // unmarshal into struct type 1
  10. case 2:
  11. // unmarshal into struct type 2
  12. default:
  13. // throw err
  14. }
  15. // or if you don't have access to type:
  16. if jsonValue["connectionID"] != nil {
  17. // unmarshal into struct type 1
  18. }
  19. return nil
  20. }

或者你可以尝试(严格地)解组到每个结构体,直到不再出现错误,例如:

  1. func DetermineStruct(d []byte) int {
  2. var connStatus *ConnStatus
  3. reader := bytes.NewReader(d)
  4. decoder := json.NewDecoder(reader)
  5. decoder.DisallowUnknownFields()
  6. err := decoder.Decode(connStatus)
  7. if err == nil {
  8. panic(err)
  9. }
  10. err = json.Unmarshal(d, &connStatus)
  11. if err == nil {
  12. return 1
  13. }
  14. var ohlcSuccess OHLCsuccess
  15. err = json.Unmarshal(d, &ohlcSuccess)
  16. if err == nil {
  17. return 2
  18. }
  19. }
英文:

Are you in control of the different responses? If so, wow about adding a "type" field to the top level?

See "How to put everything at the top level" section on https://eagain.net/articles/go-dynamic-json/ for more info.

E.g. (untested):

  1. func UnmarshalJSON(d []byte) error {
  2. var jsonValue map[string]interface{}
  3. err := json.Unmarshal(d, &jsonValue)
  4. if err != nil {
  5. return err
  6. }
  7. switch jsonValue["type"] {
  8. case 1:
  9. // unmarshal into struct type 1
  10. case 2:
  11. // unmarshal into struct type 2
  12. default:
  13. // throw err
  14. }
  15. // or if you don't have access to type:
  16. if jsonValue["connectionID"] != nil {
  17. // unmarshal into struct type 1
  18. }
  19. return nil
  20. }

Alternatively you could try to (strictly) unmarshal into each struct, until you don't get an error, e.g. something like:

  1. func DetermineStruct(d []byte) int {
  2. var connStatus *ConnStatus
  3. reader := bytes.NewReader(d)
  4. decoder := json.NewDecoder(reader)
  5. decoder.DisallowUnknownFields()
  6. err := decoder.Decode(connStatus)
  7. if err == nil {
  8. panic(err)
  9. }
  10. err = json.Unmarshal(d, &connStatus)
  11. if err == nil {
  12. return 1
  13. }
  14. var ohlcSuccess OHLCsuccess
  15. err = json.Unmarshal(d, &ohlcSuccess)
  16. if err == nil {
  17. return 2
  18. }
  19. }

huangapple
  • 本文由 发表于 2022年4月10日 16:29:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/71814937.html
匿名

发表评论

匿名网友

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

确定