如何解析没有定义结构的 JSON 数据?

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

How to parse json without a defined stucture?

问题

我想解析这个JSON:

{
        "error": null,
        "id": "tutu",
        "result": {
                "param1": 559,
                "param2": "yo",
                "param3": {"tab":["a", "b"], "param4":"hello"},
 
        }
}

问题是,如果JSON结构发生变化,我希望有一个灵活的解决方案:我希望能够使用键系统(类似于JavaScript)访问每个字段,而不需要预先知道JSON的结构:

fmt.Println(jsonObj["result"]["param3"]["tab"][1])

是否可以实现这个目标?

英文:

I want to parse this JSON :

{
        "error": null,
        "id": "tutu",
        "result": {
                "param1": 559,
                "param2": "yo",
                "param3": {"tab":["a", "b"], "param4":"hello"},
 
        }
}

The problem is that I want a flexible solution if the JSON structure changes: I want to be able to access each field with a key system (like in Javascript) without knowing in advance the JSON structure:

fmt.Println(jsonObj["result"]["param3"]["tab"][1])

Is it possible to do it ?

答案1

得分: 2

是的,虽然需要断言类型,但是这是可能的,尽管需要断言类型会使代码有些笨重:

package main

import (
    "encoding/json"
    "fmt"
)

func main() {
    b := []byte(`
    {
        "error": null,
        "id": "tutu",
        "result": {
                "param1": 559,
                "param2": "yo",
                "param3": {"tab":["a", "b"], "param4": "hello"}
        }
    }
    `)

    var jsonObj interface{}

    err := json.Unmarshal(b, &jsonObj)

    if err != nil {
        panic(err)
    }

    msg := jsonObj.(map[string]interface{})
    result := msg["result"].(map[string]interface{})
    param3 := result["param3"].(map[string]interface{})
    tab := param3["tab"].([]interface{})

    fmt.Println(tab[1])
}

这将打印出:

b

就像你期望的那样。

请注意,此程序会引发 panic:不仅在 JSON 解析失败时会引发 panic,而且在 JSON 与程序的期望不完全匹配时也会引发 panic,例如缺少键、不同的类型等。

Go 博客上的 JSON 和 Go 文章中的使用接口处理通用 JSON部分有一个在运行时检查实际类型的示例;正如评论中建议的那样,很容易忘记这一点。

英文:

Yes, it is possible, although the fact that you need to assert the types makes it a bit unwieldy:

package main

import (
    "encoding/json"
    "fmt"
)

func main() {
    b := []byte(`
    {
        "error": null,
        "id": "tutu",
        "result": {
                "param1": 559,
                "param2": "yo",
                "param3": {"tab":["a", "b"], "param4": "hello"}
        }
    }
    `)

    var jsonObj interface{}

    err := json.Unmarshal(b, &jsonObj)

    if err != nil {
        panic(err)
    }

    msg := jsonObj.(map[string]interface{})
    result := msg["result"].(map[string]interface{})
    param3 := result["param3"].(map[string]interface{})
    tab := param3["tab"].([]interface{})

    fmt.Println(tab[1])
}

This prints

b

like you would expect.

Note that this program will panic: not just if the JSON fails to parse, but also if the JSON does not exactly match the program's expectations: missing keys, different types and so on.

The Generic JSON with interface section of the JSON and Go article on The Go Blog has an example of checking the actual type of the thing at runtime; as was suggested in the comments, it's easy to forget.

答案2

得分: 2

首先,在你的情况下,可以直接将JSON解组成map[string]interface{},以避免断言。

var jsonObj map[string]interface{}
err := json.Unmarshal(b, &jsonObj)

其次,请记得检查断言。

result, ok := jsonObj["result"].(map[string]interface{})
if !ok {
    panic("not json obj")
}

param3, ok := result["param3"].(map[string]interface{})
if !ok {
    panic("not json obj")
}

tab, ok := param3["tab"].([]interface{})
if !ok {
    panic("not json arr")
}

最后,你可以声明一个新类型,并在其方法中“隐藏”断言。

type AnyObj map[string]interface{}

func (obj AnyObj) MustObject(name string) AnyObj {
    v, ok := obj[name].(map[string]interface{})
    if !ok {
        panic("not json obj")
    }
    return AnyObj(v)
}

func (obj AnyObj) MustArray(name string) []interface{} {
    v, ok := obj[name].([]interface{})
    if !ok {
        panic("not json arr")
    }
    return v
}

然后像这样使用:

func main() {
    var jsonObj AnyObj
    err := json.Unmarshal(b, &jsonObj)
    if err != nil {
        panic(err)
    }

    tab := jsonObj.MustObject("result").MustObject("param3").MustArray("tab")

    fmt.Println(tab[1])
}

在这里检查:https://play.golang.org/p/ucdMZ0VEKcr

英文:

First, in your case unmarshall directly into map[string]interface{} to save an assertion.

    var jsonObj map[string]interface{}
    err := json.Unmarshal(b, &jsonObj)

Second, remember to check the assertion

    result, ok := jsonObj["result"].(map[string]interface{})
    if !ok {
        panic("not json obj")
    }

    param3, ok := result["param3"].(map[string]interface{})
    if !ok {
        panic("not json obj")
    }

    tab, ok := param3["tab"].([]interface{})
    if !ok {
        panic("not json arr")
    }

Lastly, you can declare a new type and "hide" the assertions in its methods

type AnyObj map[string]interface{}

func (obj AnyObj) MustObject(name string) AnyObj {
    v, ok := obj[name].(map[string]interface{})
    if !ok {
        panic("not json obj")
    }
    return AnyObj(v)
}

func (obj AnyObj) MustArray(name string) []interface{} {
    v, ok := obj[name].([]interface{})
    if !ok {
        panic("not json arr")
    }
    return v
}

Then use like this:

func main() {
    var jsonObj AnyObj
    err := json.Unmarshal(b, &jsonObj)
    if (err != nil) {
        panic(err);
    }

    tab := jsonObj.MustObject("result").MustObject("param3").MustArray("tab")

    fmt.Println(tab[1])    
}

check it here https://play.golang.org/p/ucdMZ0VEKcr

答案3

得分: 1

这是我使用的 fastjson 示例代码:

package main

import (
	"fmt"
	"github.com/valyala/fastjson"
)

type ParseValue struct {
	*fastjson.Value
}

func Parse(b []byte) (*ParseValue, error) {
	var p fastjson.Parser
	v, err := p.ParseBytes(b)
	if err != nil {
		return nil, err
	}
	return &ParseValue{v}, nil
}

func (p *ParseValue) GetString(keys ...string) string {
	return string(p.GetStringBytes(keys...))
}

func main() {
	b := []byte(`
    {
        "error": null,
        "id": "tutu",
        "result": {
                "param1": 559,
                "param2": "yo",
                "param3": {"tab":["a", "b"], "param4": "hello"}
        }
    }
    `)

	v, err := Parse(b)
	if err != nil {
		panic(err)
	}

	r := v.GetString("result", "param3", "tab", "1")
	fmt.Println(r)

	rr := v.GetUint("result", "param1")
	fmt.Println(rr)

	// 输出:
	// b
	// 559
}

希望这对你有帮助!

英文:

this is mine used fastjson:

package main

import (
	"fmt"
	"github.com/valyala/fastjson"
)

type ParseValue struct {
	*fastjson.Value
}

func Parse(b []byte) (*ParseValue, error) {
	var p fastjson.Parser
	v, err := p.ParseBytes(b)
	if err != nil {
		return nil, err
	}
	return &ParseValue{v}, nil
}

func (p *ParseValue) GetString(keys ...string) string {
	return string(p.GetStringBytes(keys...))
}

func main() {
	b := []byte(`
    {
        "error": null,
        "id": "tutu",
        "result": {
                "param1": 559,
                "param2": "yo",
                "param3": {"tab":["a", "b"], "param4": "hello"}
        }
    }
    `)

	v, err := Parse(b)
	if err != nil {
		panic(err)
	}

	r := v.GetString("result", "param3", "tab", "1")
	fmt.Println(r)

	rr := v.GetUint("result", "param1")
	fmt.Println(rr)

	// output:
	// b
	// 559
}

huangapple
  • 本文由 发表于 2021年9月10日 17:29:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/69130150.html
匿名

发表评论

匿名网友

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

确定