web-dev-qa-db-ja.com

Go unmarshalで複雑なJSONを解析する方法は?

goでは、標準パッケージ encoding/json は、JSONを解析するためのjson.Unmarshal関数を公開しています。

事前定義されたstructでJSON文字列を非整列化するか、interface{}を使用して、予期しないJSONデータ構造の結果を繰り返すことができます。

とはいえ、複雑なJSONを適切に解析することはできません。誰かがこれを達成する方法を教えてもらえますか?

 {
     "k1" : "v1", 
     "k2" : "v2", 
     "k3" : 10, 
     "result" : [
                 [
                 ["v4", v5, {"k11" : "v11", "k22" : "v22"}]
                 , ... , 
                 ["v4", v5, {"k33" : "v33", "k44" : "v44"}
                 ]
                 ], 
                 "v3"
                ] 
}
11
wells

JSON and Go からの引用:

このデータの構造を知らなくても、Unmarshalを使用してデータをinterface {}値にデコードできます。

b := []byte(`{
   "k1" : "v1", 
   "k3" : 10,
   result:["v4",12.3,{"k11" : "v11", "k22" : "v22"}]
}`)
var f interface{}
err := json.Unmarshal(b, &f)

この時点で、fのGo値は、キーが文字列であり、値自体が空のインターフェイス値として格納されているマップになります。

f = map[string]interface{}{
    "k1": "v1",
    "k3":  10,
    "result": []interface{}{
       "v4",
       12.3,
       map[string]interface{}{
           "k11":"v11",
           "k22":"v22",
       },
    },
}

このデータにアクセスするには、型アサーションを使用してfの基になるmap [string] interface {}にアクセスできます。

m := f.(map[string]interface{})

次に、rangeステートメントを使用してマップを反復処理し、タイプスイッチを使用して、具体的なタイプとしてその値にアクセスできます。

for k, v := range m {
    switch vv := v.(type) {
    case string:
        fmt.Println(k, "is string", vv)
    case int:
        fmt.Println(k, "is int", vv)
    case []interface{}:
        fmt.Println(k, "is an array:")
        for i, u := range vv {
            fmt.Println(i, u)
        }
    default:
        fmt.Println(k, "is of a type I don't know how to handle")
    }
}

このようにして、型安全性の利点を享受しながら、未知のJSONデータを処理できます。

GoとJSONの詳細については、元の記事を参照してください。コードスニペットを少し変更して、質問のJSONに近づけました。

35
Johan Wikström

標準ライブラリの使用_encoding/json_パッケージ

私は JSON and Go の記事を扱ってきましたが、_case int_が機能せず、今は_case float64_である必要があり、ネストがたくさんあることがわかりました。実世界のJSON。

_> go version
go version go1.7.1 darwin/AMD64
_

GoでのJSONデコード も調べましたが、mrubyバインディングへの一連の呼び出しに手続き的に変換する必要があったため、あまり役に立ちませんでした。その記事の作成者ほとんどの部分でGo構造体に満足しています。

私はこれをいじりながら少し過ごしましたが、最終的な反復ダンパー関数は次のようになりました。

_func dumpJSON(v interface{}, kn string) {
    iterMap := func(x map[string]interface{}, root string) {
        var knf string
        if root == "root" {
            knf = "%q:%q"
        } else {
            knf = "%s:%q"
        }
        for k, v := range x {
            dumpJSON(v, fmt.Sprintf(knf, root, k))
        }
    }

    iterSlice := func(x []interface{}, root string) {
        var knf string
        if root == "root" {
            knf = "%q:[%d]"
        } else {
            knf = "%s:[%d]"
        }
        for k, v := range x {
            dumpJSON(v, fmt.Sprintf(knf, root, k))
        }
    }

    switch vv := v.(type) {
    case string:
        fmt.Printf("%s => (string) %q\n", kn, vv)
    case bool:
        fmt.Printf("%s => (bool) %v\n", kn, vv)
    case float64:
        fmt.Printf("%s => (float64) %f\n", kn, vv)
    case map[string]interface{}:
        fmt.Printf("%s => (map[string]interface{}) ...\n", kn)
        iterMap(vv, kn)
    case []interface{}:
        fmt.Printf("%s => ([]interface{}) ...\n", kn)
        iterSlice(vv, kn)
    default:
        fmt.Printf("%s => (unknown?) ...\n", kn)
    }
}
_

bは、トップレベルの配列またはオブジェクトのいずれかを表すJSONを持つバイトスライスであるため、次のように呼び出すことができます。

_var f interface{}
if err := json.Unmarshal(b, &f); err != nil {
    panic(err)
}
dumpJSON(f, "root")
_

これがお役に立てば幸いです。ここで完全なプログラムを試してください

他のパッケージの使用

Goタイプがどのように機能するかを学ぶ必要があり、reflectを使用すると宇宙のマスターのように感じる場合を除いて、自分でそれを行わないことをお勧めします(個人的にはreflectは私を怒らせます)。

@ changingrainbows以下で指摘 のように、 _github.com/tidwall/gjson_ パッケージがあります。これは_encoding/json_をラップしているように見え、reflectを使用します。私は_github.com/mitchellh/reflectwalk_と似ていないかもしれません。これは非常に使いにくく、内部の動作はかなり複雑です。

私は自分のプロジェクトの1つで _github.com/buger/jsonparser_ をかなり広範囲に使用しましたが、まだ試していませんが _github.com/json-iterator/go_ もあります_github.com/buger/jsonparser_に基づいているようで、_enconding/json_互換のインターフェイスを公開しているようで、 func Get(data []byte, path ...interface{}) Any もあります。ちなみに、Kubernetesプロジェクトは最近_github.com/json-iterator/go_に切り替わりました。私のプロジェクトでは、_encoding/json_と_github.com/buger/jsonparser_を使用していますが、時間があればおそらく_github.com/json-iterator/go_に切り替えます。私はより多くの発見でこの投稿を更新しようとします。

4
errordeveloper

最近では、 gjson はJSONでプロパティの選択を提供します

k1 := gjson.Get(json, "k1")
k33 : = gjson.Get(json, "result.#.#.k33")
4