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

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

Unmarshalling Dynamic JSON Data With Overlapping Fields in Golang

问题

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

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

响应1{"connectionID":17973829270596587247,"event":"systemStatus","status":"online","version":"1.9.0"}
响应2{"channelID":328,"channelName":"ohlc-5","event":"subscriptionStatus","pair":"XBT/USD","status":"subscribed","subscription":{"interval":5,"name":"ohlc"}}
响应3[328,["1649576721.042916","1649577000.000000","42641.50000","42641.50000","42641.50000","42641.50000","42641.50000","0.00335101",2],"ohlc-5","XBT/USD"]
响应4{"event":"heartbeat"}

以下是结构体

import (
	"time"
	"encoding/json"
)

type ConnStatus struct {
	ConnectionID	uint64	`json:"connectionID"`
	Event		string	`json:"event"`
	Status		string	`json:"status"`
	Version		string	`json:"version"`
}

type HeartBeat struct {
	Event	string	`json:"event"`
}

type OHLCsuccess struct {
	ChannelID		int		`json:"channelID"`
	ChannelName		string	`json:"channelName"`
	Event			string	`json:"event"`
	Pair			string	`json:"pair"`
	Status			string	`json:"status"`
	Subscription	OHLC	`json:"subscription"`
}

type OHLC struct {
	Interval	int		`json:"interval"`
	Name		string	`json:"name"`
}

type OHLCUpdates struct {
	ChannelID		int
	OHLCArray		OHLCNewTrade
	ChannelName		string
	Pair			string
}

type OHLCNewTrade struct {
	StartTime		UnixTime
	EndTime			UnixTime
	Open			float64
	High			float64
	Low				float64
	Close			float64
	VWAP			float64
	Volume			float64
	Count			int
}

type UnixTime struct {
	time.Time
}

func (u *UnixTime) UnmarshalJSON(d []byte) error {
	var ts int64
	err := json.Unmarshal(d, &ts)
	if err != nil {
		return err
	}
	u.Time = time.Unix(ts, 0).UTC()
	return nil
}

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

英文:

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.

response 1: {"connectionID":17973829270596587247,"event":"systemStatus","status":"online","version":"1.9.0"}
response 2: {"channelID":328,"channelName":"ohlc-5","event":"subscriptionStatus","pair":"XBT/USD","status":"subscribed","subscription":{"interval":5,"name":"ohlc"}}
response 3: [328,["1649576721.042916","1649577000.000000","42641.50000","42641.50000","42641.50000","42641.50000","42641.50000","0.00335101",2],"ohlc-5","XBT/USD"]
response 4: {"event":"heartbeat"}
structs below
import (
"time"
"encoding/json"
)
type ConnStatus struct {
ConnectionID		uint64			`json:"connectionID"`
Event				string			`json:"event"`
Status 				string			`json:"status"`
Version				string			`json:"version"`
}
type HeartBeat struct {
Event				string			`json:"event"`
}
type OHLCsuccess struct {
ChannelID			int				`json:"channelID"`
ChannelName			string			`json:"channelName"`
Event				string			`json:"event"`
Pair				string			`json:"pair"`
Status				string			`json:"status"`
Subscription		OHLC			`json:"subscription"`
}
type OHLC struct {
Interval		int			`json:"interval"`
Name			string		`json:"name"`
}
type OHLCUpdates struct {
ChannelID			int
OHLCArray			OHLCNewTrade
ChannelName			string
Pair				string
}
type OHLCNewTrade struct {
StartTime 			UnixTime
EndTime				UnixTime
Open				float64
High				float64
Low					float64
Close				float64
VWAP				float64
Volume				float64
Count				int
}
type UnixTime struct {
time.Time
}
func (u *UnixTime) UnmarshalJSON(d []byte) error {
var ts int64
err := json.Unmarshal(d, &ts)
if err != nil {
return err
}
u.Time = time.Unix(ts, 0).UTC()
return nil
}

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"部分获取更多信息。

例如(未经测试):

func UnmarshalJSON(d []byte) error {
	var jsonValue map[string]interface{}
	err := json.Unmarshal(d, &jsonValue)

	if err != nil {
		return err
	}

	switch jsonValue["type"] {
	case 1:
		// unmarshal into struct type 1
	case 2:
		// unmarshal into struct type 2
	default:
		// throw err
	}

	// or if you don't have access to type:
	if jsonValue["connectionID"] != nil {
		// unmarshal into struct type 1
	}

	return nil
}

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

func DetermineStruct(d []byte) int {
	var connStatus *ConnStatus

	reader := bytes.NewReader(d)
	decoder := json.NewDecoder(reader)
	decoder.DisallowUnknownFields()

	err := decoder.Decode(connStatus)
	if err == nil {
		panic(err)
	}

	err = json.Unmarshal(d, &connStatus)
	if err == nil {
		return 1
	}

	var ohlcSuccess OHLCsuccess
	err = json.Unmarshal(d, &ohlcSuccess)
	if err == nil {
		return 2
	}
}
英文:

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

func UnmarshalJSON(d []byte) error {
	var jsonValue map[string]interface{}
	err := json.Unmarshal(d, &jsonValue)

	if err != nil {
		return err
	}

	switch jsonValue["type"] {
	case 1:
		// unmarshal into struct type 1
	case 2:
		// unmarshal into struct type 2
	default:
		// throw err
	}

	// or if you don't have access to type:
	if jsonValue["connectionID"] != nil {
		// unmarshal into struct type 1
	}

	return nil
}

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

func DetermineStruct(d []byte) int {
	var connStatus *ConnStatus

	reader := bytes.NewReader(d)
	decoder := json.NewDecoder(reader)
	decoder.DisallowUnknownFields()

	err := decoder.Decode(connStatus)
	if err == nil {
		panic(err)
	}

	err = json.Unmarshal(d, &connStatus)
	if err == nil {
		return 1
	}

	var ohlcSuccess OHLCsuccess
	err = json.Unmarshal(d, &ohlcSuccess)
	if err == nil {
		return 2
	}
}

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:

确定