2016-04-26 94 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 

} 

可我知道我可以代替GeneralConfig使用嵌套結構,但這需要我知道的有效載荷,即確切的結構它不適用於不同的鍵(我將被鎖定到「important_key」)。Golang類型轉換/斷言問題與解組JSON

當我不知道「important_key」的價值是什麼時,是否有golang解決方法?我說golang,因爲如果可能的話,可以要求所有「important_keys」擁有一個常數父鍵,這可以解決這個問題。總結一下,給定一個任意的json對象,必須有一種方法可以遍歷它的鍵,並且如果一個值是一個自定義類型,則將該值轉換爲該類型。現在看來,如果我使用類型轉換,它告訴我,類型是interface{},我需要使用類型斷言;然而,如果我使用類型斷言,它告訴我,interface{}map[string]interface{}而不是main.GeneralConfig

+6

我的建議是找出模式。沒有「任意」json這樣的東西,我真的厭惡那些採取低劣方法的人,因爲他們認爲他們的json是如此不可預測。我有消息給你,這不是!我用模式解析了更多變體和複雜的json,並沒有那麼難。 json是一種形式語言,有一個確切的模式來描述任何blob,它們可以非常靈活。 – evanmcdonnal

+0

當你有一個嵌套的json結構並解組它時,最終會得到一個map [string] interface {}。接口{}部分(如果它是嵌套部分)又是map [string] interface {}。所以你可以繼續遍歷,直到你發現你的界面不是地圖。如果你提供了一個你想使用的json的樣本,我們可以提出一個解決方案。 –

+0

@evanmcdonnal這更像是一個'理論上,我該怎麼做?'爲了實際目的,我得出了和你一樣的結論。 –

回答

2

我同意關於試圖利用傳入JSON的預期結構以寫出明確定義的Structs的意見,但我仍會嘗試回答這個問題。

從您看到的內容打印出來的東西與您看到的錯誤消息相比,編譯器知道的類型少於運行時,因爲運行時可以查看實際值。爲了使編譯器達到速度,我們必須(i)聲明(*jsonData)["important_key"]map[string]interface{} - 編譯器只知道它是interface{} - 然後(ii)將其類型轉換爲GeneralConfig類型。請參閱:

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 

此示例只佔基礎數據是字符串類型,但你可以對期望接收的任何類型的開關,以及類似的任何開關,就可以分組你的案例,所以你可以像所有的數字一樣處理。