2015-03-31 7 views
5

Si consideri il seguente codice:Looping/iterate sopra il secondo livello nidificato 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" 
     } 
    } 
    ` 

ho qualche requisito come voglio leggere/ottenere tutta la chiave e il valore in String tipo per qualche adn trasformazione Non riesco a definire lo struct perché otterrò l'input JSON dinamico (ad es. InnerInnerArray come stringa, quindi il ciclo di secondo livello mi fornirà l'indice dell'array ed elaborerò ogni JSON con la chiave fld1 e val1).

Desidero ripetere tutte le coppie chiave/valore contenute al suo interno, qual è il modo più efficiente di attraversare la mappa?

Nota: I Am Newbie per Go-lang, il tuo suggerimento/miglioramento sulla domanda è anche il benvenuto.

risposta

9

Vedi this blog che copre a fondo questo tema, in particolare la sezione decodifica dati arbitrari. Usando che si può fare qualcosa di simile: (playground example)

package main 

import (
    "encoding/json" 
    "fmt"  
) 

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

    // Parsing/Unmarshalling JSON encoding/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" 
    } 
} 
` 

questo stampa:

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

La cosa fondamentale è che si deve utilizzare il tipo asserzione quando si lavora con tipi di interfaccia. Il selettore di caratteri semplifica la determinazione del tipo secondo necessità. Il codice passerà ricorsivamente attraverso qualsiasi array o mappa nidificati in modo da poter aggiungere tutti i livelli desiderati e ottenere tutti i valori.

1

Avrete bisogno di analizzare il JSON e quindi di ricorrere attraverso la struttura ispezionando i tipi dei valori contenuti e gestendoli in qualche modo.

La funzione esempio sotto accetta un *interface{} (puntatore a qualsiasi tipo) e una funzione di gestione di stringa, int e puntatori all'oggetto al quale cede gli elementi che esso scopre:

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(&k, nil, &v) 
     eachJsonValue(&v, handler) 
    } 
    } 
    // Yield each index/value for arrays. 
    a, isArray := (*obj).([]interface{}) 
    if isArray { 
    for i, x := range a { 
     handler(nil, &i, &x) 
     eachJsonValue(&x, handler) 
    } 
    } 
    // Do nothing for primitives since the handler got them. 
} 

Calling come illustrato di seguito stamperà i risultati elencati. La vostra funzione di gestione potrebbe, ovviamente, fare qualcosa di speciale con la chiave nota/valori come "fld1":

func main() { 
    // Parse the JSON. 
    var obj interface{} 
    json.Unmarshal([]byte(input), &obj) // XXX: check the error value. 

    // Handle object key/value pairs and array index/items. 
    eachJsonValue(&obj, func(key *string, index *int, value *interface{}) { 
    if key != nil { // It's an object key/value pair... 
     fmt.Printf("OBJ: key=%q, value=%#v\n", *key, *value) 
    } else { // It's an array item... 
     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" 
1

Ci sono domande correlate here e here (ed eventualmente altri).

Esistono alcune API di analisi JSON più sofisticate che semplificano il lavoro. Un esempio è stretchr/objx.

Un esempio di utilizzo objx:

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

questo funziona quando davvero non si sa quale sia la struttura JSON sarà. Tuttavia, se si conosce la struttura dell'input JSON prima del tempo, il modo migliore è descriverlo con le strutture e utilizzare l'API standard per il marshalling.

Problemi correlati