英文:
How to unmarshall an array of different types correctly?
问题
只要我有键值对,解组就很简单,但是如何解组一个不同类型的数组并且顺序不同呢?单个元素是明确定义和已知的,但是顺序不确定。
我想不出一个优雅的解决方案。
我应该尝试所有元素吗?
是否有一种可以为我做到这一点的联合类型?
package main
import (
    "encoding/json"
    "fmt"
)
var my_json string = `{
    "an_array":[
        "with_a string",
        {
            "and":"some_more",
            "different":["nested", "types"]
        }
    ]
}`
type MyInner struct {
    And     string
    Different   []string
}
type MyJSON struct {
    An_array []json.RawMessage
}
func main() {
    var my_json_test MyJSON
    e := json.Unmarshal([]byte(my_json), &my_json_test)
    if e != nil {
        fmt.Println(e)
    } else {
        for index, value := range my_json_test.An_array {
            fmt.Println("index: ", index)
            fmt.Println("value: ", string(value))
        }
        var my_inner MyInner
        err := json.Unmarshal(my_json_test.An_array[1], &my_inner)
        if err != nil {
            fmt.Println(err)
        } else {
            fmt.Println("inner structure: ", my_inner)
        }
    }
}
英文:
As long as I have key-value pairs unmarshalling is pretty straight forward but how would I unmarshall an array of different types in different order? The single elements are well defined and known but the order is not.
I can not come up with a beautiful solution.
Would I try and error over all elements?
Is there some kind of union type that could do that for me?
package main
import (
    "encoding/json"
    "fmt"
)
var my_json string = `{
    "an_array":[
        "with_a string",
        {
            "and":"some_more",
            "different":["nested", "types"]
        }
    ]
}`
type MyInner struct {
    And     string
    Different   []string
}
type MyJSON struct {
    An_array []json.RawMessage
}
func main() {
    var my_json_test MyJSON
    e := json.Unmarshal([]byte(my_json), &my_json_test)
    if e != nil {
        fmt.Println(e)
    } else {
        for index, value := range my_json_test.An_array {
            fmt.Println("index: ", index)
            fmt.Println("value: ", string(value))
        }
        var my_inner MyInner
        err := json.Unmarshal(my_json_test.An_array[1], &my_inner)
        if err != nil {
            fmt.Println(err)
        } else {
            fmt.Println("inner structure: ", my_inner)
        }
    }
}
答案1
得分: 22
Go官方博客有一篇关于encoding/json的好文章:JSON和GO。可以将任意数据解码为interface{},并使用类型断言来动态确定类型。
你的代码可以修改为以下形式:
package main
import (
    "encoding/json"
    "fmt"
)
var my_json string = `{
    "an_array":[
    "with_a string",
    {
        "and":"some_more",
        "different":["nested", "types"]
    }
    ]
}`
func WTHisThisJSON(f interface{}) {
    switch vf := f.(type) {
    case map[string]interface{}:
        fmt.Println("是一个map:")
        for k, v := range vf {
            switch vv := v.(type) {
            case string:
                fmt.Printf("%v: 是字符串 - %q\n", k, vv)
            case int:
                fmt.Printf("%v: 是整数 - %q\n", k, vv)
            default:
                fmt.Printf("%v: ", k)
                WTHisThisJSON(v)
            }
        }
    case []interface{}:
        fmt.Println("是一个数组:")
        for k, v := range vf {
            switch vv := v.(type) {
            case string:
                fmt.Printf("%v: 是字符串 - %q\n", k, vv)
            case int:
                fmt.Printf("%v: 是整数 - %q\n", k, vv)
            default:
                fmt.Printf("%v: ", k)
                WTHisThisJSON(v)
            }
        }
    }
}
func main() {
    fmt.Println("JSON:\n", my_json, "\n")
    var f interface{}
    err := json.Unmarshal([]byte(my_json), &f)
    if err != nil {
        fmt.Println(err)
    } else {
        fmt.Printf("JSON: ")
        WTHisThisJSON(f)
    }
}
它的输出如下:
JSON:
 {
    "an_array":[
    "with_a string",
    {
        "and":"some_more",
        "different":["nested", "types"]
    }
    ]
} 
JSON: 是一个map:
an_array: 是一个数组:
0: 是字符串 - "with_a string"
1: 是一个map:
and: 是字符串 - "some_more"
different: 是一个数组:
0: 是字符串 - "nested"
1: 是字符串 - "types"
它还不完整,但展示了它将如何工作。
英文:
Go official blog has a nice article about encoding/json: JSON and GO. It's possible to "Decode arbitrary data" into an interface{} and use type assertion to determine the type dynamically.
Your code can be probably modified to this:
package main
import (
    "encoding/json"
    "fmt"
)
var my_json string = `{
    "an_array":[
    "with_a string",
    {
        "and":"some_more",
        "different":["nested", "types"]
    }
    ]
}`
func WTHisThisJSON(f interface{}) {
    switch vf := f.(type) {
    case map[string]interface{}:
        fmt.Println("is a map:")
        for k, v := range vf {
            switch vv := v.(type) {
            case string:
                fmt.Printf("%v: is string - %q\n", k, vv)
            case int:
                fmt.Printf("%v: is int - %q\n", k, vv)
            default:
                fmt.Printf("%v: ", k)
                WTHisThisJSON(v)
            }
        }
    case []interface{}:
        fmt.Println("is an array:")
        for k, v := range vf {
            switch vv := v.(type) {
            case string:
                fmt.Printf("%v: is string - %q\n", k, vv)
            case int:
                fmt.Printf("%v: is int - %q\n", k, vv)
            default:
                fmt.Printf("%v: ", k)
                WTHisThisJSON(v)
            }
        }
    }
}
func main() {
    fmt.Println("JSON:\n", my_json, "\n")
    var f interface{}
    err := json.Unmarshal([]byte(my_json), &f)
    if err != nil {
        fmt.Println(err)
    } else {
        fmt.Printf("JSON: ")
        WTHisThisJSON(f)
    }
}
It gives output as follows:
JSON:
 {
    "an_array":[
    "with_a string",
    {
        "and":"some_more",
        "different":["nested", "types"]
    }
    ]
} 
JSON: is a map:
an_array: is an array:
0: is string - "with_a string"
1: is a map:
and: is string - "some_more"
different: is an array:
0: is string - "nested"
1: is string - "types"
It's not complete yet, but shows how it's gonna work.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论