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"
]
}
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に近づけました。
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
_に切り替えます。私はより多くの発見でこの投稿を更新しようとします。
最近では、 gjson はJSONでプロパティの選択を提供します
k1 := gjson.Get(json, "k1")
k33 : = gjson.Get(json, "result.#.#.k33")