2015-03-31 20 views
5

Rozważmy następujący kod:zapętlanie/iteracyjne nad drugim poziomie zagnieżdżone JSON w podróży 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" 
     } 
    } 
    ` 

mam pewne wymagania jak chcę czytać/dostać cały klucz i wartość w String typu jakiegoś ADN przetwórczym Nie mogę zdefiniować struct, ponieważ otrzymam dynamiczne wejście JSON (np. InnerInnerArray jako ciąg, następnie pętla drugiego poziomu da mi indeks tablicy i przetworzy każdy JSON o kluczu fld1 i val1).

Chciałbym przetestować każdą parę klucz/wartość w niej zawartą, jaki jest najskuteczniejszy sposób poruszania się po mapie?

Uwaga: Jestem nowicjuszem w Go-lang, twoja sugestia/poprawa na pytanie jest również mile widziana.

Odpowiedz

9

Zobacz this wpis w blogu, który dokładnie uwzględnia tę kwestię, a konkretnie rozdział dekodowania dowolnych danych. Korzystanie że można zrobić coś takiego: (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" 
    } 
} 
` 

ten wypisze:

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

Kluczową rzeczą jest to, że trzeba użyć typu twierdzenie podczas pracy z typów interfejsów. Przełącznik typu ułatwia określenie typu w razie potrzeby. Kod będzie rekursywnie przechodzić przez dowolną zagnieżdżoną tablicę lub mapę, dzięki czemu możesz dodać dowolną liczbę poziomów i uzyskać wszystkie wartości.

1

Będziesz musiał przeanalizować JSON, a następnie powrócić do struktury, sprawdzając typy zawartych wartości i postępując z nimi w pewien sposób.

Przykładowa funkcja poniżej bierze *interface{} (wskaźnik do dowolnego typu) oraz funkcję obsługi string, int oraz wskaźników obiektów, do których otrzymuje się elementy ujawnią:

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. 
} 

Nazywając to jak wykazano poniżej wydrukuje listę wyników. Twoja funkcja obsługi mogłyby, oczywiście, zrobić coś specjalnego ze znanym klucz/wartości, takie jak „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

Istnieją powiązane pytania: here i here (i ewentualnie inne).

Istnieje kilka bardziej wyrafinowanych interfejsów API analizowania JSON, które ułatwiają pracę. Przykładem jest stretchr/objx.

Przykład użycia objx:

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

To działa, kiedy naprawdę nie wiem, co będzie struktura JSON. Jednakże, jeśli znasz strukturę danych wejściowych JSON z wyprzedzeniem, preferowanym sposobem jest opisanie go za pomocą struktur i użycie standardowego interfejsu API do zestawiania.

Powiązane problemy