我目前正在处理进入我的应用程序的json对象流,并且在确定解析它们的最佳方法时遇到了一些困难。流由具有已定义类型的对象组成。问题是对象中的一个字段是更改类型。它看起来像这样:
[{
"status": "closed",
"type": "transaction",
"transaction": {
"TransactionType": "TypeA",
"Account": "Some string",
"Fee": "14",
"date": 45325680
},
"validated": true
},
{
"status": "closed",
"type": "transaction",
"transaction": {
"TransactionType" : "TypeB",
"Account" : "Some string",
"Fee": "42",
"Destination" : "Some string"
},
"validated": true
}]
你可以看到"父母"不会改变,但"交易"确实。我从"交易"中删除了很多字段。字段使其更容易解释,但这是一个具有10-ish公共字段和10-ish更改字段的类型,这些字段取决于类型。还有10种事务类型,这使得将所有内容放入一个结构并拥有大量可选字段非常烦人。我也在考虑每个事务类型的一个结构,但这不起作用,因为那时无法指定该字段在父类中应该具有的类型。
我如何有效地解析这些对象?不幸的是,我无法改变流出来的元素的结构。解决这个问题的最佳方法是什么?
答案 0 :(得分:0)
或许将transaction
作为map[string]interface{}
示例https://play.golang.com/p/j_u_ztw04M
package main
import (
"encoding/json"
"fmt"
)
type Stream []struct {
Status string `json:"status"`
Type string `json:"type"`
// map instead of struct
Transaction map[string]interface{} `json:"transaction"`
Validated bool `json:"validated"`
}
func main() {
stream := Stream{}
err := json.Unmarshal(rawj, &stream)
if err != nil {
fmt.Println("error:", err)
return
}
for i, s := range stream {
fmt.Println("thing:", i)
for k, v := range s.Transaction {
// do manual stuff here, perhaps long switch on k and/or
// as suggested by Cerise Limón type switch on v
fmt.Printf("key: %s, val: %v, val type: %T\n", k, v, v)
}
fmt.Println()
}
}
var rawj = []byte(`[{
"status": "closed",
"type": "transaction",
"transaction": {
"TransactionType": "TypeA",
"Account": "Some string",
"Fee": "14",
"date": 45325680
},
"validated": true
},
{
"status": "closed",
"type": "transaction",
"transaction": {
"TransactionType" : "TypeB",
"Account" : "Some string",
"Fee": "42",
"Destination" : "Some string"
},
"validated": true
}]`)
玩得开心!
答案 1 :(得分:0)
对于"交易" struct我会使公共密钥直接类型(例如string
,int
等)和可选的密钥指针到它们的类型(*string
,{{ 1}}等等,用" omitempty"标记,如果它们不存在于事件中,则设置为nil:
*int
这样公共字段总是在那里,但是如果存在则可能填充默认(空)值和可选字段,如果省略则填充为nil:
type Transaction struct {
// Common fields are direct types...
TransactionType string
Account string
Fee string
// Optional fields are pointers with "omitempty"...
Date *int `json:",omitempty"`
Destination *string `json:",omitempty"`
}
答案 2 :(得分:0)
为每种交易类型定义结构,并分两步执行取消编组。首先使用
type Stream []struct {
...
TransactionRaw json.RawMessage `json:"transaction"`
...
}
然后使用Regexp.FindStringSubmatch
上的TransactionRaw
来确定第二次调用json.Unmarshal
的类型
type Transaction struct {
TransactionType string `json:"TransactionType"`
Account string `json:"Account"`
Fee string `json:"Fee"`
}
type TypeA struct {
Transaction
Date int `json:"date"`
}
等...
答案 3 :(得分:0)
我认为使用反射是[]map[string]interface{}
在this answer中,我举了一个关于使用reflect来解码改变类型的例子。