5

我想尝试从 Go 中的 JSON 获取键值,但是我不确定如何去做。

我已经能够使用 simplejson 来读取 json 值,但是我无法找出如何获取键值。

有人能指出我正确的方向和/或帮助我吗?

谢谢!

4

4 回答 4

18

您可以通过执行以下操作获取 JSON 结构的顶级键:

package main

import (
    "encoding/json"
    "fmt"
)

// your JSON structure as a byte slice
var j = []byte(`{"foo":1,"bar":2,"baz":[3,4]}`)

func main() {

    // a map container to decode the JSON structure into
    c := make(map[string]json.RawMessage)

    // unmarschal JSON
    e := json.Unmarshal(j, &c)

    // panic on error
    if e != nil {
        panic(e)
    }

    // a string slice to hold the keys
    k := make([]string, len(c))

    // iteration counter
    i := 0

    // copy c's keys into k
    for s, _ := range c {
        k[i] = s
        i++
    }

    // output result to STDOUT
    fmt.Printf("%#v\n", k)

}

请注意,键的顺序不能与它们在 JSON 结构中的顺序相对应。它们在最终切片中的顺序甚至会在完全相同的代码的不同运行之间有所不同。这是因为地图迭代的工作方式。

于 2013-07-03T16:17:11.240 回答
11

如果你不想写几十个无用的结构,你可以使用类似https://github.com/tidwall/gjson的东西:

gjson.Get(
  `{"object": {"collection": [{"items": ["hello"]}]}}`,
  "object.collection.items.0",
) // -> "hello"

加上一些奇怪有用的查询技巧

于 2016-05-19T20:02:49.230 回答
3

尽管这个问题很老并且以不同的方式解决,但我遇到了一个类似的问题并且没有找到一个简单的解决方案。我只需要一个巨大的 json 响应中的所有值。

我的方法:简单地在给定的字符串上使用正则表达式,在这种情况下是 JSON 格式的字符串。

纯字符串会针对给定键进行过滤,并通过此方法仅返回给定键的值

// extracts the value for a key from a JSON-formatted string
// body - the JSON-response as a string. Usually retrieved via the request body
// key - the key for which the value should be extracted
// returns - the value for the given key
func extractValue(body string, key string) string {
    keystr := "\"" + key + "\":[^,;\\]}]*"
    r, _ := regexp.Compile(keystr)
    match := r.FindString(body)
    keyValMatch := strings.Split(match, ":")
    return strings.ReplaceAll(keyValMatch[1], "\"", "")
}

关于给定的模式,我没有测试所有大小写,但它正在扫描这样的字符串,如双引号、键的名称、双引号、分号和除“,”“;”之外的任何字符序列 "}" "]" (所以基本上任何可以关闭 json 语法中的键值对的东西)

例子:

jsonResp := "{\"foo\":\"bar\"}"    
value := extractValue(jsonResp, "foo")
fmt.Println(value)

会简单的返回

我看到的主要优点是,您不需要关心 JSON 响应的结构,而只需按键获取所需的值。

注意:我认为只能获取第一个匹配键的值。但是您始终可以修改该方法。它只是利用了正则表达式技术。

于 2020-06-16T11:08:29.137 回答
0

我使用以下内容从 JSON 中获取嵌套键:

import (
    "bytes"
    "encoding/json"
    "errors"
    "io"
    "sort"
)

func keys(b []byte) ([]string, error) {
    dec := json.NewDecoder(bytes.NewBuffer(b))
    // store unique keys
    kmap := make(map[string]struct{})
    // is the next Token a key?
    var key bool
    // keep track of both object and array parents with a slice of bools:
    //   - an object parent is true, an array parent is false
    parents := make([]bool, 0, 10)
    for {
        t, err := dec.Token()
        if err == io.EOF {
            break
        }
        if err != nil {
            return nil, err
        }
        del, ok := t.(json.Delim)
        if ok {
            if del == '{' {
                // push an object parent
                parents = append(parents, true)
            }
            if del == '[' {
                // push an array parent
                parents = append(parents, false)
            }
            if del == '}' || del == ']' {
                if len(parents) == 0 {
                    return nil, errors.New("bad json: unexpected } or ] delim")
                }
                // pop the last parent
                parents = parents[:len(parents)-1]
            }
            if len(parents) > 0 && parents[len(parents)-1] {
                // if we are within an object, the next token must be a key
                key = true
            } else {
                // otherwise we are in an array, and the next token is an array entry
                key = false
            }
            continue
        }
        if key {
            str, ok := t.(string)
            if !ok {
                return nil, errors.New("bad json: keys must be strings")
            }
            kmap[str] = struct{}{}
            // if this is a key, then the next token is the value
            key = false
        } else if len(parents) > 0 && parents[len(parents)-1] {
            // if this is a value, and we are within an object, then the next token is a new key
            key = true
        }
    }
    // now turn our map of keys into a sorted slice
    ret := make([]string, len(kmap))
    var i int
    for k := range kmap {
        ret[i] = k
        i++
    }
    sort.Strings(ret)
    return ret, nil
}
于 2018-04-12T07:59:51.910 回答