使用反射在 Go 中动态解析 json
最近在用 Go 写一个对接某个 API 的 Adapter 时碰到了一点关于 json 反序列化的问题。对方通过 websocket 传递 json 来进行推送消息,但是推送的格式不太统一,而且种类繁多,难以使用少量通用的结构体类型来对这些消息进行反序列化。不过好在这些消息中都有一个共同的 type 字段用于标识该 json 对象的类型,不至于手足无措。在经过几天的资料查询参考后,通过一些解析手段和反射完成了对推送消息的解析,做记录如下。 动态 JSON 解析 对于大量格式不同的 json 对象,最为简单粗暴的方法当然就是用万能的 interface {} 去进行解析,再复杂的 json 对象也能解析成简单的基本类型组合。这样子做虽然在解析的时候舒服了不少,但是后续访问对象字段就麻烦了,需要不断使用字典之类的访问方法对其进行访问,而且 interface {} 类型对 IDE 的智能提示也非常不友好,后续的维护也更为繁琐,容易出问题。以上问题一叠加,interface {} 自然是下下策。那么有什么办法来在运行时根据 json 对象自动选择相应类型进行序列化呢? 延迟解析 一个比较好的方法是使用 json.RawMessage 类型来延后解析。json.RawMessage 是 json 标准库中提供的一个类型,预先被定义为这个类型的字段在 json.Unmarshal 时,json 库将不会对这个字段进行递归解析,而是将这个字段对应的 raw json 数据存入该字段,并可用于后续解析。 下面用一个简单的例子来说明这个方法,假设我们的接口会返回如下两种格式的 json: // Fruit json { "type": "fruit", "data": { "name": "orange", "color": "red", "taste": "sweet" } } //vegetable json { "type": "vegetable", "data": { "name": "corn", "size": "big", "juicy": false } } 以上两种格式的 json 的 data 字段下存放的 json 对象格式不同,而 data 字段所存放的 json 格式由 type 字段规定。这就给了我们一个思路,在首次解析中保留 data 的 RawMessage 数据,然后通过解析出来的 type 字段值来确定使用哪个静态类型对 data 字段的 RawMessage 进行解析。解析的代码可能看起来像这个样子: ...