Looping/iterate over the second level nested JSON in go lang

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

Looping/iterate over the second level nested JSON in go lang

问题

考虑以下代码:

package main

import (
	"encoding/json"
	"fmt"
	"reflect"
)


func main() {  
	//Creating the maps for JSON
	m := map[string]interface{}{}

	//Parsing/Unmarshalling JSON encoding/json
	err := json.Unmarshal([]byte(input), &m)

	fmt.Println("\nReflect type of Parsing/Unmarshalling Error Object:\n",reflect.TypeOf(err))
	fmt.Println("\nParsing/Unmarshalling Error Object:\n",err)
	if err != nil {
		panic(err)
	}

	fmt.Println("\nParsed JSON is as follows:\n",m)
	fmt.Println("\nReflect type of parsed json object:\n", reflect.TypeOf(m))

	for firstLvlkey, firstLvlValue := range m { 
		fmt.Println("First Level Key:", firstLvlkey)
		fmt.Println("First Level Key reflect type of :", reflect.TypeOf(firstLvlkey))

		fmt.Println("First Level Value:", firstLvlValue)
		fmt.Println("First Level Value reflect type of :", reflect.TypeOf(firstLvlValue))
		// <===============================>
		//Here I want to iterate/loop over innerJSON1, InnerJSON2 then reach to level InnerInnerJSONArray - fld1 and fld2
		// <===============================>

	}
}

const input = `
{
	"outterJSON":{
		"innerJSON1":{
			"value1":10,
			"value2":22
			,
			"InnerInnerArray": [ "test1" , "test2"],
			"InnerInnerJSONArray": [ {"fld1" : "val1"} , {"fld2" : "val2"} ]
			},
			"InnerJSON2":"NoneValue"
		}
	}
	`

我有一些要求,我想以String类型读取/获取所有的键和值,以进行一些处理,但我不能定义struct,因为我将获得动态的JSON输入(例如,InnerInnerArray作为字符串,然后第二层循环将给我数组的索引,并处理具有键fld1val1的每个JSON)。

我希望迭代其中包含的每个键/值对,最有效的方法是什么?

注意:我是Go语言的新手,欢迎您对问题提出建议/改进。

英文:

Consider the following code:

package main

import (
&quot;encoding/json&quot;
&quot;fmt&quot;
&quot;reflect&quot;
)


func main() {  
	//Creating the maps for JSON
	m := map[string]interface{}{}

	//Parsing/Unmarshalling JSON encoding/json
	err := json.Unmarshal([]byte(input), &amp;m)

	fmt.Println(&quot;\nReflect type of Parsing/Unmarshalling Error Object:\n&quot;,reflect.TypeOf(err))
	fmt.Println(&quot;\nParsing/Unmarshalling Error Object:\n&quot;,err)
	if err != nil {
		panic(err)
	}

	fmt.Println(&quot;\nParsed JSON is as follows:\n&quot;,m)
	fmt.Println(&quot;\nReflect type of parsed json object:\n&quot;, reflect.TypeOf(m))

	for firstLvlkey, firstLvlValue := range m { 
		fmt.Println(&quot;First Level Key:&quot;, firstLvlkey)
		fmt.Println(&quot;First Level Key reflect type of :&quot;, reflect.TypeOf(firstLvlkey))

		fmt.Println(&quot;First Level Value:&quot;, firstLvlValue)
		fmt.Println(&quot;First Level Value reflect type of :&quot;, reflect.TypeOf(firstLvlValue))
         // &lt;===============================&gt;
         //Here I want to iterate/loop over innerJSON1, InnerJSON2 then reach to level InnerInnerJSONArray - fld1 and fld2
         // &lt;===============================&gt;

	}
}

const input = `
{
	&quot;outterJSON&quot;:{
		&quot;innerJSON1&quot;:{
			&quot;value1&quot;:10,
			&quot;value2&quot;:22
			,
			&quot;InnerInnerArray&quot;: [ &quot;test1&quot; , &quot;test2&quot;],
			&quot;InnerInnerJSONArray&quot;: [ {&quot;fld1&quot; : &quot;val1&quot;} , {&quot;fld2&quot; : &quot;val2&quot;} ]
			},
			&quot;InnerJSON2&quot;:&quot;NoneValue&quot;
		}
	}
	`

I have some requirement like I want to read/get all the Key and value in String type for some processing adn I can't define the struct because I will be getting dynamic JSON input (e.g InnerInnerArray as a string then second level loop will give me index of array and process each JSON having key fld1 and val1).

I wish to iterate over every key/value pair contained within it, what is the most efficient way of going through the map?

Note: I am Newbie for Go-lang, your suggestion/improvement on question is also most welcome.

答案1

得分: 27

请看这篇博客文章,它详细介绍了这个主题,特别是解码任意数据部分。使用这个方法,你可以像这样做:
示例代码

package main

import (
    "encoding/json"
    "fmt"    
)

func main() {
    // 创建用于 JSON 的映射
    m := map[string]interface{}{}

    // 解析/反序列化 JSON
    err := json.Unmarshal([]byte(input), &m)

    if err != nil {
        panic(err)
    }
    parseMap(m)
}

func parseMap(aMap map[string]interface{}) {
    for key, val := range aMap {
        switch concreteVal := val.(type) {
        case map[string]interface{}:
            fmt.Println(key)
            parseMap(val.(map[string]interface{}))
        case []interface{}:
            fmt.Println(key)
            parseArray(val.([]interface{}))
        default:
            fmt.Println(key, ":", concreteVal)
        }
    }
}

func parseArray(anArray []interface{}) {
    for i, val := range anArray {
        switch concreteVal := val.(type) {
        case map[string]interface{}:
            fmt.Println("Index:", i)
            parseMap(val.(map[string]interface{}))
        case []interface{}:
            fmt.Println("Index:", i)
            parseArray(val.([]interface{}))
        default:
            fmt.Println("Index", i, ":", concreteVal)

        }
    }
}

const input = `
{
    "outterJSON": {
        "innerJSON1": {
            "value1": 10,
            "value2": 22,
            "InnerInnerArray": [ "test1" , "test2"],
            "InnerInnerJSONArray": [{"fld1" : "val1"} , {"fld2" : "val2"}]
        },
        "InnerJSON2":"NoneValue"
    }
}
`

这段代码会打印出:

//outterJSON
//innerJSON1
//InnerInnerJSONArray
//Index: 0
//fld1 : val1
//Index: 1
//fld2 : val2
//value1 : 10
//value2 : 22
//InnerInnerArray
//Index 0 : test1
//Index 1 : test2
//InnerJSON2 : NoneValue

关键是在处理接口类型时必须使用类型断言。类型切换使得根据需要确定类型变得容易。代码会递归遍历任何嵌套的数组或映射,因此你可以添加任意多层级并获取所有的值。

英文:

See this blog entry which thoroughly covers this subject, specifically the section Decoding arbitrary data. Using that you can do something like this:
(playground example)

package main
import (
&quot;encoding/json&quot;
&quot;fmt&quot;    
)
func main() {
// Creating the maps for JSON
m := map[string]interface{}{}
// Parsing/Unmarshalling JSON encoding/json
err := json.Unmarshal([]byte(input), &amp;m)
if err != nil {
panic(err)
}
parseMap(m)
}
func parseMap(aMap map[string]interface{}) {
for key, val := range aMap {
switch concreteVal := val.(type) {
case map[string]interface{}:
fmt.Println(key)
parseMap(val.(map[string]interface{}))
case []interface{}:
fmt.Println(key)
parseArray(val.([]interface{}))
default:
fmt.Println(key, &quot;:&quot;, concreteVal)
}
}
}
func parseArray(anArray []interface{}) {
for i, val := range anArray {
switch concreteVal := val.(type) {
case map[string]interface{}:
fmt.Println(&quot;Index:&quot;, i)
parseMap(val.(map[string]interface{}))
case []interface{}:
fmt.Println(&quot;Index:&quot;, i)
parseArray(val.([]interface{}))
default:
fmt.Println(&quot;Index&quot;, i, &quot;:&quot;, concreteVal)
}
}
}
const input = `
{
&quot;outterJSON&quot;: {
&quot;innerJSON1&quot;: {
&quot;value1&quot;: 10,
&quot;value2&quot;: 22,
&quot;InnerInnerArray&quot;: [ &quot;test1&quot; , &quot;test2&quot;],
&quot;InnerInnerJSONArray&quot;: [{&quot;fld1&quot; : &quot;val1&quot;} , {&quot;fld2&quot; : &quot;val2&quot;}]
},
&quot;InnerJSON2&quot;:&quot;NoneValue&quot;
}
}
`

This will print:

    //outterJSON
//innerJSON1
//InnerInnerJSONArray
//Index: 0
//fld1 : val1
//Index: 1
//fld2 : val2
//value1 : 10
//value2 : 22
//InnerInnerArray
//Index 0 : test1
//Index 1 : test2
//InnerJSON2 : NoneValue

The key thing is that you have to use type assertion when working with interface types. The type switch makes it easy to determine the type as needed. The code will recursively range through any nested array or map so you can add as many levels as you wish and get all your values.

答案2

得分: 5

这里有相关的问题这里这里(可能还有其他)。

还有一些更复杂的JSON解析API可以让你的工作更轻松。一个例子是stretchr/objx

使用objx的示例:

document, err := objx.FromJSON(json)
// TODO 处理错误
document.Get("path.to.field[0].you.want").Str()

当你真的不知道JSON结构时,这个方法很有效。然而,如果你事先知道JSON输入的结构,最好的方法是使用结构体描述它,并使用标准的API进行编组。

英文:

There are related questions here and here (and possibly others).

There are some more sophisticated JSON parsing APIs that make your job easier. An example is stretchr/objx.

An example of using objx:

document, err := objx.FromJSON(json)
// TODO handle err
document.Get(&quot;path.to.field[0].you.want&quot;).Str()

This works when you really don't know what the JSON structure will be. However, if you do know the structure of your JSON input ahead of time, the preferred way is to describe it with structs and use the standard API for marshalling.

答案3

得分: 2

你需要解析JSON,然后递归遍历结构,检查包含值的类型,并以某种方式处理它们。

下面是一个示例函数,它接受一个*interface{}(任意类型的指针)和一个处理程序函数,该函数接受字符串、整数和对象指针,并返回它发现的项:

func eachJsonValue(obj *interface{}, handler func(*string, *int, *interface{})) {
  if obj == nil {
    return
  }
  // 遍历对象的所有键/值对
  o, isObject := (*obj).(map[string]interface{})
  if isObject {
    for k, v := range o {
      handler(&k, nil, &v)
      eachJsonValue(&v, handler)
    }
  }
  // 遍历数组的每个索引/值
  a, isArray := (*obj).([]interface{})
  if isArray {
    for i, x := range a {
      handler(nil, &i, &x)
      eachJsonValue(&x, handler)
    }
  }
  // 对于基本类型,不做任何操作,因为处理程序已经处理了它们
}

按照下面的示例调用它将打印出列出的结果。你的处理程序函数可以根据已知的键/值(如"fld1")执行特殊操作:

func main() {
  // 解析JSON
  var obj interface{}
  json.Unmarshal([]byte(input), &obj) // XXX: 检查错误值。

  // 处理对象的键/值对和数组的索引/项
  eachJsonValue(&obj, func(key *string, index *int, value *interface{}) {
    if key != nil { // 它是对象的键/值对...
      fmt.Printf("OBJ: key=%q, value=%#v\n", *key, *value)
    } else { // 它是数组的项...
      fmt.Printf("ARR: index=%d, value=%#v\n", *index, *value)
    }
  })
}

// OBJ: key="outterJSON", value=map[string]interface {}{...}
// OBJ: key="innerJSON1", value=map[string]interface {}{...}
// OBJ: key="value1", value=10
// OBJ: key="value2", value=22
// OBJ: key="InnerInnerArray", value=[]interface {}{...}
// ARR: index=0, value="test1"
// ARR: index=1, value="test2"
// OBJ: key="InnerInnerJSONArray", value=[]interface {}{...}
// ARR: index=0, value=map[string]interface {}{...}
// OBJ: key="fld1", value="val1"
// ARR: index=1, value=map[string]interface {}{...}
// OBJ: key="fld2", value="val2"
// OBJ: key="InnerJSON2", value="NoneValue"
英文:

You'll need to parse the JSON and then recurse through the structure inspecting the types of the contained values and handling them in some way.

The example function below takes an *interface{} (pointer to any type) and a handler function of string, int, and object pointers to which it yields the items it discovers:

func eachJsonValue(obj *interface{}, handler func(*string, *int, *interface{})) {
if obj == nil {
return
}
// Yield all key/value pairs for objects.
o, isObject := (*obj).(map[string]interface{})
if isObject {
for k, v := range o {
handler(&amp;k, nil, &amp;v)
eachJsonValue(&amp;v, handler)
}
}
// Yield each index/value for arrays.
a, isArray := (*obj).([]interface{})
if isArray {
for i, x := range a {
handler(nil, &amp;i, &amp;x)
eachJsonValue(&amp;x, handler)
}
}
// Do nothing for primitives since the handler got them.
}

Calling it as demonstrated below will print the listed results. Your handler function could, of course, do something special with known key/values such as "fld1":

func main() {
// Parse the JSON.
var obj interface{}
json.Unmarshal([]byte(input), &amp;obj) // XXX: check the error value.
// Handle object key/value pairs and array index/items.
eachJsonValue(&amp;obj, func(key *string, index *int, value *interface{}) {
if key != nil { // It&#39;s an object key/value pair...
fmt.Printf(&quot;OBJ: key=%q, value=%#v\n&quot;, *key, *value)
} else { // It&#39;s an array item...
fmt.Printf(&quot;ARR: index=%d, value=%#v\n&quot;, *index, *value)
}
})
}
// OBJ: key=&quot;outterJSON&quot;, value=map[string]interface {}{...}
// OBJ: key=&quot;innerJSON1&quot;, value=map[string]interface {}{...}
// OBJ: key=&quot;value1&quot;, value=10
// OBJ: key=&quot;value2&quot;, value=22
// OBJ: key=&quot;InnerInnerArray&quot;, value=[]interface {}{...}
// ARR: index=0, value=&quot;test1&quot;
// ARR: index=1, value=&quot;test2&quot;
// OBJ: key=&quot;InnerInnerJSONArray&quot;, value=[]interface {}{...}
// ARR: index=0, value=map[string]interface {}{...}
// OBJ: key=&quot;fld1&quot;, value=&quot;val1&quot;
// ARR: index=1, value=map[string]interface {}{...}
// OBJ: key=&quot;fld2&quot;, value=&quot;val2&quot;
// OBJ: key=&quot;InnerJSON2&quot;, value=&quot;NoneValue&quot;

答案4

得分: 0

我已经实现了与https://stackoverflow.com/users/1078890/iamnan解决方案非常相似的东西。parseMapparseArray的主体被合并为一个,看起来像这样。

func printJson(res1 map[string]interface{}, res2 []interface{}) {
	for k, v := range res1 {
		switch vv := v.(type) {
		case float64, int, string:
			fmt.Println(k, ":", vv)
		case []interface{}:
			fmt.Println(k, ":")
			printJson(nil, vv)
		case map[string]interface{}:
			fmt.Println(k, ":")
			printJson(vv, nil)
		default:
			fmt.Println(k, ":", vv)
		}
	}
	for k, v := range res2 {
		switch vv := v.(type) {
		case float64, int, string:
			fmt.Println(k, ":", vv)
		case []interface{}:
			fmt.Println(k, ":")
			printJson(nil, vv)
		case map[string]interface{}:
			fmt.Println(k, ":")
			printJson(vv, nil)
		default:
			fmt.Println(k, ":", vv)
		}
	}
}

代码可以在这里找到:https://gist.github.com/sandeep-sarkar/78a0e96461b4dec727386a96404d29b0

英文:

I have implemented something very similar to https://stackoverflow.com/users/1078890/iamnan solution. The body of parseMap and parseArray is combined into one and it looks something like this.

func printJson(res1 map[string]interface{}, res2 []interface{}) {
for k, v := range res1 {
switch vv := v.(type) {
case float64, int, string:
fmt.Println(k, &quot;:&quot;, vv)
case []interface{}:
fmt.Println(k, &quot;:&quot;)
printJson(nil, vv)
case map[string]interface{}:
fmt.Println(k, &quot;:&quot;)
printJson(vv, nil)
default:
fmt.Println(k, &quot;:&quot;, vv)
}
}
for k, v := range res2 {
switch vv := v.(type) {
case float64, int, string:
fmt.Println(k, &quot;:&quot;, vv)
case []interface{}:
fmt.Println(k, &quot;:&quot;)
printJson(nil, vv)
case map[string]interface{}:
fmt.Println(k, &quot;:&quot;)
printJson(vv, nil)
default:
fmt.Println(k, &quot;:&quot;, vv)
}
}
}

Code can be found here https://gist.github.com/sandeep-sarkar/78a0e96461b4dec727386a96404d29b0

huangapple
  • 本文由 发表于 2015年3月31日 18:10:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/29366038.html
匿名

发表评论

匿名网友

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

确定