2016-04-26 9 views
0
package main 

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

type GeneralConfig map[string]interface{} 

var data string = ` 
{ 
    "key":"value", 
    "important_key": 
     {"foo":"bar"} 
}` 

func main() { 
    jsonData := &GeneralConfig{} 
    json.Unmarshal([]byte(data), jsonData) 

    fmt.Println(reflect.TypeOf(jsonData)) //main.GeneralConfig 

    jsonTemp := (*jsonData)["important_key"] 
    fmt.Println(reflect.TypeOf(jsonTemp)) //map[string]interface {} 

    //newGeneralConfig := GeneralConfig(jsonTemp) 
    //cannot convert jsonTemp (type interface {}) to type GeneralConfig: 
    //need type assertion 

    newGeneralConfig := jsonTemp.(GeneralConfig) 
    //fmt.Println(reflect.TypeOf(newGeneralConfig)) 
    //panic: interface conversion: interface {} is map[string]interface {}, 
    //not main.GeneralConfig 

} 
the playground

非整列化JSONとGolangタイプ変換/アサーション発行し

の利用可能な

すなわち、私はGeneralConfigの代わりに、ネストされた構造体を使用することができることを理解し、それはペイロードの正確な構造を知っている私を必要とします別のキーでは機能しません(私は "important_key"にロックされます)。

「important_key」の値がわからない場合のゴーラ対策はありますか?可能であれば、すべての "important_keys"にはこの問題を解決するための定数の親キーを持たせる必要があるため、golangと言います。要約すると、任意のjsonオブジェクトでは、キーをトラバースできる方法が必要であり、値がカスタムタイプの場合はその値をそのタイプに変換します。今のところ、タイプ変換を使用すると、タイプがinterface{}であることがわかり、タイプアサーションを使用する必要があります。ただし、タイプアサーションを使用すると、interface{}map[string]interface{}ではなく、main.GeneralConfigであることがわかります。

+6

私の助言は、スキーマを把握することです。 「任意の」jsonのようなものはありません。私は彼らのjsonがとても予測不可能だと考えるので、劣ったアプローチを取っている人々の本当の病気です。私はあなたのためのニュースを持っています、それはそうではありません!私はスキーマを使ってはるかに多くの変形と複雑なjsonを解析しましたが、それほど難しくありませんでした。 jsonは形式的な言語ですが、ブロブを記述するための正確なスキーマがあり、非常に柔軟性があります。 – evanmcdonnal

+0

ネストされたjson構造体を持ち、それを非整列化すると、map [string]インタフェース{}で終了します。interface {}部分(ネスト部分の場合)は、map [string]インターフェース{}です。したがって、あなたのインターフェース{}がマップではないことがわかるまで、トラバースを続けることができます。使用したいjsonのサンプルを提供する場合は、解決策を見つけることができます。 –

+0

@evanmcdonnalこれは理論上のもので、これはどうしたらいいですか?実用的な目的のために、私はあなたと同じ結論に達しました。 –

答えて

2

よく定義されたStructsを書き込むために着信JSONの予想される構造を利用しようとしているコメントについては同意しますが、私はとにかくその質問に答えようとします。

表示されているエラーメッセージと表示されているエラーメッセージを取り除くことは、実行時に実際の値を見ることができるため、コンパイラは実行時よりも型が分かりにくいことです。コンパイラを最高速度にするには、(*jsonData)["important_key"]map[string]interface{}であることをアサートする必要があります - コンパイラはそれをinterface{}であるとしか認識せず、次に(の型キャスト)します。参照:

package main 

import (
    "fmt" 
    "encoding/json" 
) 

type GeneralConfig map[string]interface{} 

func main() { 
    jsonStruct := new(GeneralConfig) 
    json.Unmarshal([]byte(`{"parent_key": {"foo": "bar"}}`), jsonStruct) 
    fmt.Printf("%#v\n", jsonStruct) 
    // => &main.GeneralConfig{"parent_key":map[string]interface {}{"foo":"bar"}} 

    nestedStruct := (*jsonStruct)["parent_key"] 
    fmt.Printf("%#v\n", nestedStruct) 
    // => map[string]interface {}{"foo":"bar"} 
    // Whilst this shows the runtime knows its actual type is 
    // map[string]interface, the compiler only knows it to be an interface{}. 

    // First we assert for the compiler that it is indeed a 
    // map[string]interface{} we are working with. You can imagine the issues 
    // that might arrise if we has passed in `{"parent_key": 123}`. 
    mapConfig, ok := nestedStruct.(map[string]interface{}) 
    if !ok { 
     // TODO: Error-handling. 
    } 

    // Now that the compiler can be sure mapConfig is a map[string]interface{} 
    // we can type-cast it to GeneralConfig: 
    config := GeneralConfig(mapConfig) 
    fmt.Printf("%#v\n", config) 
    // => main.GeneralConfig{"foo":"bar"} 
} 
+0

説明/回答ありがとう: –

+0

うれしい私は助けることができました。他のところで以前にコメントされていたように、親コンテナを表す型を作成することもできます。 'struct OuterConfig {Config * GeneralConfig \' json: "parent_key" \ '}'です。 – icio

0

あなたはjson.RawMessageを探しています。
他の値に基づいてアンマーシャリングを遅延させ、強制的に非マーシャル化して特定の型にすることができます。

これはお勧めできませんが、お探しのものに近い場合があります。

http://play.golang.org/p/PWwAUDySE0

0

あなたは後にしている何を得る場合、これは標準の「回避策」です。未知のデータを扱う際には、このパターンを(あなたの例から変更して)再帰的に切り替えて、未知のjsonデータ本体の具体的な値に到達させることができます。

package main 

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

var data = ` 
{ 
    "key":"value", 
    "important_key": 
     {"foo":"bar"} 
}` 

func main() { 
    var jsonData interface{} 
    json.Unmarshal([]byte(data), &jsonData) 

    fmt.Println(reflect.TypeOf(jsonData)) 

    parseArbitraryJSON(jsonData.(map[string]interface{})) 
} 

func parseArbitraryJSON(data map[string]interface{}) { 
    for k, v := range data { 
     switch a := v.(type) { 
     case string: 
      fmt.Printf("%v:%v\n", k, a) 
     case map[string]interface{}: 
      fmt.Printf("%v:%v\n", k, a) 
      parseArbitraryJSON(a) 
     } 
    } 
} 

出力結果は次のとおりです。

map[string]interface {} 
key:value 
important_key:map[foo:bar] 
foo:bar 

この例は、基本データは文字列型であることを考慮していますが、あなたが受け取ることを期待あらゆるタイプに切り替えることができ、どのようにすることができます切り替えますあなたのケースをグループ化するので、すべての数字を同様に扱うことができます。

関連する問題