为了账号安全,请及时绑定邮箱和手机立即绑定

正确的 json 取消在 Go 中使用空接口进行取消编组

正确的 json 取消在 Go 中使用空接口进行取消编组

Go
繁星coding 2022-09-12 20:23:56
我目前正在学习golang,(可能和我之前的许多其他人一样)我试图正确理解空界面。作为一个练习,我正在阅读Postman生成的一个大json文件,并试图只访问一个字段(在众多可用的字段中)。以下是json的简单表示形式,没有我不想阅读的不必要的字段(但仍然存在):{    "results": [        {            "times": [                1,                2,                3,                4                ]        }    ]}由于 json 对象很大,我选择不使用自定义结构取消封接,而是决定使用空接口interface{}一段时间后,我设法得到了一些工作代码,但我非常确定这不是正确的方法。byteValue, _ := ioutil.ReadAll(jsonFile)var result map[string]interface{}err = json.Unmarshal(byteValue, &result)if err != nil {    log.Fatalln(err)}// ESPECIALLY UGLYr := result["results"].([]interface{})r1 := r[0].(map[string]interface{})r2 := r1["times"].([]interface{})times := make([]float64, len(r2))for i := range r2 {    times[i] = r2[i].(float64)}有没有更好的方法来浏览我的json对象,而不必每次我越来越深入到对象时实例化新变量?
查看完整描述

2 回答

?
翻阅古今

TA贡献1780条经验 获得超5个赞

  1. 即使 JSON 很大,您也只需定义您实际关心的字段

  2. 仅当密钥不是有效的 Go 标识符(在本例中为密钥是标识)时,才需要使用 JSON 标记,即使这样,有时也可以使用map[string]something

  3. 除非您需要子s用于某些功能或其他功能,否则您不需要定义它们struct

  4. 除非您需要重用该类型,否则您甚至不必定义它,您只需在声明时定义结构即可

例:

package main


import (

   "encoding/json"

   "fmt"

)


const s = `

{

   "results": [

      {

         "times": [1, 2, 3, 4]

      }

   ]

}

`


func main() {

   var t struct {

      Results []struct {

         Times []int

      }

   }

   json.Unmarshal([]byte(s), &t)

   fmt.Printf("%+v\n", t) // {Results:[{Times:[1 2 3 4]}]}

}


查看完整回答
反对 回复 2022-09-12
?
拉风的咖菲猫

TA贡献1995条经验 获得超2个赞

[...]尝试仅访问一个字段(在众多可用字段中)。

对于这个具体的用例,我将使用一个库来查询和访问已知路径中的单个值,例如:

https://github.com/jmespath/go-jmespath

另一方面,如果您正在练习如何访问JSON中的嵌套值,我建议您尝试编写一个递归函数,该函数以与go-jmespath相同(但简单)的方式遵循未知结构中的路径。

好吧,我挑战了自己,花了一个小时写这篇文章。它的工作原理。不确定性能或错误,它真的很有限:)

https://play.golang.org/p/dlIsmG6Lk-p

package main


import (

    "encoding/json"

    "errors"

    "fmt"

    "strings"

)


func main() {


    // I Just added a bit more of data to the structure to be able to test different paths

    fileContent := []byte(`

    {"results": [

            {"times": [

                    1,

                    2,

                    3,

                    4

        ]},

        {"times2": [

                    5,

                    6,

                    7,

                    8

        ]},

        {"username": "rosadabril"},

        {"age": 42},

        {"location": [41.5933262, 1.8376757]}

        

    ],

    "more_results": {

        "nested_1": {

            "nested_2":{

                "foo": "bar"

            }

        }

    }

    }`)


    var content map[string]interface{}

    if err := json.Unmarshal(fileContent, &content); err != nil {

        panic(err)

    }


    // some paths to test

    valuePaths := []string{

        "results.times",

        "results.times2",

        "results.username",

        "results.age",

        "results.doesnotexist",

        "more_results.nested_1.nested_2.foo",

    }


    for _, p := range valuePaths {

        breadcrumbs := strings.Split(p, ".")


        value, err := search(breadcrumbs, content)

        if err != nil {

            fmt.Printf("\nerror searching '%s': %s\n", p, err)

            continue

        }


        fmt.Printf("\nFOUND A VALUE IN: %s\n", p)

        fmt.Printf("Type: %T\nValue: %#v\n", value, value)

    }


}


// search is our fantastic recursive function! The idea is to search in the structure in a very basic way, for complex querying use jmespath

func search(breadcrumbs []string, content map[string]interface{}) (interface{}, error) {

    // we should never hit this point, but better safe than sorry and we could incurr in an out of range error (check line 82)

    if len(breadcrumbs) == 0 {

        return nil, errors.New("ran out of breadcrumbs :'(")

    }


    // flag that indicates if we are at the end of our trip and whe should return the value without more checks

    lastBreadcrumb := len(breadcrumbs) == 1


    // current breadcrumb is always the first element.

    currentBreadcrumb := breadcrumbs[0]


    if value, found := content[currentBreadcrumb]; found {

        if lastBreadcrumb {

            return value, nil

        }

        // if the value is a map[string]interface{}, go down the rabbit hole, recursion!

        if aMap, isAMap := value.(map[string]interface{}); isAMap {

            // we are calling ourselves popping the first breadcrumb and passing the current map

            return search(breadcrumbs[1:], aMap)

        }

        // if it's an array of interfaces the thing gets complicated :(

        if anArray, isArray := value.([]interface{}); isArray {

            for _, something := range anArray {

                if aMap, isAMap := something.(map[string]interface{}); isAMap && len(breadcrumbs) > 1 {

                    if v, err := search(breadcrumbs[1:], aMap); err == nil {

                        return v, nil

                    }

                }

            }

        }

    }

    return nil, errors.New("woops, nothing here")

}


查看完整回答
反对 回复 2022-09-12
  • 2 回答
  • 0 关注
  • 82 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信