3 回答
TA贡献1795条经验 获得超7个赞
里面table_contents有两个类型选项(json 对象或 json 对象列表)。您可以做的是解组到一个接口,然后在使用它时对其运行类型检查:
type MyStruct1 struct {
TableContents []interface{} `json:"table_contents"`
}
...
for i := range container.TableContents {
switch container.TableContents[i].(type){
case map[string]interface{}:
fmt.Println("json object")
case []interface{}:
fmt.Println("list")
}
}
从那里您可以使用一些库(例如https://github.com/mitchellh/mapstructure)将未编组的结构映射到您的TableContentClass类型。在此处查看 PoC 游乐场:https: //play.golang.org/p/NhVUhQayeL_C
TA贡献1890条经验 获得超9个赞
自定义UnmarshalJSON函数
您还可以在具有 2 种可能性的对象上创建自定义 UnmarshalJSON 函数。在你的情况下,那将是TableContentUnion.
在自定义解组器中,您可以决定如何解组内容。
func (s *TableContentUnion) UnmarshalJSON(b []byte) error {
// Note that we get `b` as bytes, so we can also manually check to see
// if it is an array (starts with `[`) or an object (starts with `{`)
var jsonObj interface{}
if err := json.Unmarshal(b, &jsonObj); err != nil {
return err
}
switch jsonObj.(type) {
case map[string]interface{}:
// Note: instead of using json.Unmarshal again, we could also cast the interface
// and build the values as in the example above
var tableContentClass TableContentClass
if err := json.Unmarshal(b, &tableContentClass); err != nil {
return err
}
s.TableContentClass = &tableContentClass
case []interface{}:
// Note: instead of using json.Unmarshal again, we could also cast the interface
// and build the values as in the example above
if err := json.Unmarshal(b, &s.TableContentClassArray); err != nil {
return err
}
default:
return errors.New("TableContentUnion.UnmarshalJSON: unknown content type")
}
return nil
}
然后其余的工作就像您之前失败的测试代码一样。这里是工作的Go Playground
解组map并手动构建结构
您始终可以将 json(在根目录下有一个对象)解组为map[string]interface{}. 然后,您可以在检查它们是什么类型后迭代并进一步解组它们。
工作示例:
func main() {
result := []byte(`{"table_contents":[[{"id":100,"description":"text100"},{"id":101,"description":"text101"}],{"id":1,"description":"text1"}]}`)
var jsonMap map[string]interface{}
err := json.Unmarshal(result, &jsonMap)
if err != nil {
fmt.Println(" [0] Error message: " + err.Error())
return
}
cts, ok := jsonMap["table_contents"].([]interface{})
if !ok {
// Note: nil or missing 'table_contents" will also lead to this path.
fmt.Println("table_contents is not a slice")
return
}
var unions []TableContentUnion
for _, content := range cts {
var union TableContentUnion
if contents, ok := content.([]interface{}); ok {
for _, content := range contents {
contCls := parseContentClass(content)
if contCls == nil {
continue
}
union.TableContentClassArray = append(union.TableContentClassArray, *contCls)
}
} else {
contCls := parseContentClass(content)
union.TableContentClass = contCls
}
unions = append(unions, union)
}
container := MyStruct1{
TableContents: unions,
}
for i := range container.TableContents {
fmt.Println(container.TableContents[i])
}
}
func parseContentClass(value interface{}) *TableContentClass {
m, ok := value.(map[string]interface{})
if !ok {
return nil
}
return &TableContentClass{
ID: int(m["id"].(float64)),
Description: m["description"].(string),
}
}
如果 json 有太多变化,这是最有用的。对于这样的情况,有时切换到工作方式不同的 json 包也可能有意义,例如https://github.com/tidwall/gjson,它根据路径获取值。
TA贡献2065条经验 获得超14个赞
使用json.RawMessage捕获 JSON 文档的不同部分。根据需要解组每个原始消息。
func (ms *MyStruct1) UnmarshalJSON(data []byte) error {
// Declare new type with same base type as MyStruct1.
// This breaks recursion in call to json.Unmarshal below.
type x MyStruct1
v := struct {
*x
// Override TableContents field with raw message.
TableContents []json.RawMessage `json:"table_contents"`
}{
// Unmarshal all but TableContents directly to the
// receiver.
x: (*x)(ms),
}
err := json.Unmarshal(data, &v)
if err != nil {
return err
}
// Unmarahal raw elements as appropriate.
for _, tcData := range v.TableContents {
if bytes.HasPrefix(tcData, []byte{'{'}) {
var v TableContentClass
if err := json.Unmarshal(tcData, &v); err != nil {
return err
}
ms.TableContents = append(ms.TableContents, v)
} else {
var v []TableContentClass
if err := json.Unmarshal(tcData, &v); err != nil {
return err
}
ms.TableContents = append(ms.TableContents, v)
}
}
return nil
}
像这样使用它:
var container MyStruct1
err := json.Unmarshal(result, &container)
if err != nil {
// handle error
}
这种方法不会添加任何外部依赖项。MyStruct1在或中添加或删除字段时,无需修改功能代码TableContentClass。
- 3 回答
- 0 关注
- 195 浏览
添加回答
举报
