如何在Go中从JSON字符串中获取键值

huangapple go评论91阅读模式
英文:

How to get the key value from a json string in Go

问题

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

我已经能够使用simplejson读取JSON值,但是我还没有找到如何获取键值的方法。

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

谢谢!

英文:

I would like to try get the key values from JSON in Go, however I'm unsure how to.

I've been able to use simplejson to read json values, however I've not been able to find out how to get the key values.

Would anyone be able to point me in the right direction and/or help me?

Thank you!

答案1

得分: 24

您可以通过以下方式获取JSON结构的顶级键:

package main

import (
    "encoding/json"
    "fmt"
)

// 将JSON结构作为字节切片
var j = []byte(`{"foo":1,"bar":2,"baz":[3,4]}`)

func main() {

    // 用于解码JSON结构的映射容器
    c := make(map[string]json.RawMessage)

    // 反序列化JSON
    e := json.Unmarshal(j, &c)

    // 出现错误时抛出异常
    if e != nil {
        panic(e)
    }

    // 用于保存键的字符串切片
    k := make([]string, len(c))

    // 迭代计数器
    i := 0

    // 将c的键复制到k中
    for s, _ := range c {
        k[i] = s
        i++
    }

    // 将结果输出到STDOUT
    fmt.Printf("%#v\n", k)

}

请注意,键的顺序不一定与JSON结构中的顺序相对应。即使是相同代码的不同运行,最终切片中键的顺序也会有所变化。这是因为映射迭代的工作方式。

英文:

You can get the top-level keys of a JSON structure by doing:

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)

}

Note that the order of the keys must not correspond to the their order in the JSON structure. Their order in the final slice will even vary between different runs of the exact same code. This is because of how map iteration works.

答案2

得分: 17

如果你不想写成堆的无用结构体,你可以使用类似于 https://github.com/tidwall/gjson 的东西:

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

还有一些奇怪但有用的查询技巧

英文:

If you don't feel like writing tens of useless structs, you could use something like https://github.com/tidwall/gjson:

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

Plus some weird-useful querying tricks.

答案3

得分: 3

即使这个问题很旧并且有不同的解决方法,但我遇到了一个类似的问题,并且没有找到一个简单的解决方案。我只需要在一个庞大的JSON响应中获取所有值中的一个。

我的方法:简单地在给定的字符串上使用正则表达式,即JSON格式的字符串。

这个方法会过滤给定键的纯字符串,并通过以下方法仅返回给定键的值:

// 从JSON格式的字符串中提取键的值
// body - JSON响应作为字符串。通常通过请求体检索
// 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)

将简单地返回bar

我认为它只能获取第一个匹配键的值。但是您可以随时修改这个方法。它只是利用了正则表达式技术的优势。

英文:

Even though the question is old and solved in different ways, I came across an similiar problem and didn't find an easy solution. I only needed 1 of all the values in a huge json-response.

My approach: Simply using a regex over an given string, in this case the JSON formatted string.

The plain string gets filtered for a given key and returns only the value for the given key via this method

// 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], "\"", "")
}

Regarding the given pattern, I didn't test all case, but it's scanning for a string like this
double quote, the name of the key, double quote, an semicolon and any char sequence except "," ";" "}" "]" (so basically anything that could close a key value pair in the syntax of json)

Example:

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

would simple return bar

The main advantage I see, that you don't need to care about the structure of the JSON-response, but go just for the value you need by key.

Note: I think it's only possible to grab the value of the first matched key. But you can always modify the method. It just makes use of the regex-technology.

答案4

得分: 0

我使用以下代码从JSON中获取嵌套键:

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

func keys(b []byte) ([]string, error) {
	dec := json.NewDecoder(bytes.NewBuffer(b))
	// 存储唯一键
	kmap := make(map[string]struct{})
	// 下一个Token是否为键?
	var key bool
	// 使用布尔值切片跟踪对象和数组的父级:
	//   - 对象父级为true,数组父级为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 == '{' {
				// 推入对象父级
				parents = append(parents, true)
			}
			if del == '[' {
				// 推入数组父级
				parents = append(parents, false)
			}
			if del == '}' || del == ']' {
				if len(parents) == 0 {
					return nil, errors.New("bad json: unexpected } or ] delim")
				}
				// 弹出最后一个父级
				parents = parents[:len(parents)-1]
			}
			if len(parents) > 0 && parents[len(parents)-1] {
				// 如果我们在对象内部,则下一个Token必须是键
				key = true
			} else {
				// 否则我们在数组中,下一个Token是数组条目
				key = false
			}
			continue
		}
		if key {
			str, ok := t.(string)
			if !ok {
				return nil, errors.New("bad json: keys must be strings")
			}
			kmap[str] = struct{}{}
			// 如果这是一个键,则下一个Token是值
			key = false
		} else if len(parents) > 0 && parents[len(parents)-1] {
			// 如果这是一个值,并且我们在对象内部,则下一个Token是新键
			key = true
		}
	}
	// 现在将我们的键映射转换为排序的切片
	ret := make([]string, len(kmap))
	var i int
	for k := range kmap {
		ret[i] = k
		i++
	}
	sort.Strings(ret)
	return ret, nil
}
英文:

I used the following to grab nested keys from 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
}

huangapple
  • 本文由 发表于 2013年7月3日 23:58:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/17452722.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定