2015-04-01 81 views
0

我有一個對象列表(olievere/Elastic SearchResult.Hits是確切的)。其中每一個都有一個json.RawMessage對象,我正在創建一個可抽象的方法,它接受任何結構的接口片,Unmarshal的每個人將'json.RawMessage'命中到所述結構中,並將其附加到傳入的[]interface中。Golang抽象接口切片轉換

這個func不應該有任何邏輯或洞察所需的業務層結構,並且數據庫調用接口相當繁重,因此無法查看上述Elastic包。我所試圖做的示例...

foo.go  
import (bar, package) 
type TestStruct struct {  
    Slice []*package.Struct // package.Struct has a value of Source which is a  
          // json.RawMessage  
}  

func GetData() bar.Test { 
    return &TestStruct{*package.GetData()} 
} 

func (result TestStruct) UnmarshalStruct(v []interface{}) {  
    for _, singleStruct := range result.Slice {  
     append(json.Unmarshal(singleStruct, &v)) 
    } 

bar.go 
type Handler interface { 
    GetData() Test 
} 

type Test interface { 
    UnmarshalStruct 
} 

type OtherType struct { 
    foo string 
    bar string 
} 

func RetrieveData() []OtherType { 
    handler := New(Handler) 
    test := handler.GetData() 
    var typeSlice []OtherType  
    test.UnmarshalStruct(&typeSlice) 
} 

我在尋找手[]OtherType類型,或任何其他新的結構我決定創建的東西第二個文件,以UnmarshalStruct,並讓它返回給我那個相同的結構,只是充滿了數據

作爲一個例子,我有兩種不同類型的數據,我將從Elastic中尋找。我將收到以下兩個對象中的一個的列表。

{ 'foo': '', 
    'id': 
} 

而且在不同的指標

{ 'bar': '', 
    'baz': '', 
    'eee': '' 
}  

每個這些自然會需要兩個不同的結構。
但是,我希望單個方法能夠解碼這些列表中的任何一個。我將在下面給出,並使用相同的功能,我希望能夠將其轉換爲bar結構,另一種類型爲foo結構。

{ 'source': [ 
    { 'bar': '', 
     'baz': '', 
     'eee': '' 
    }, 
    { 'bar': '', 
     'baz': '', 
     'eee': '' 
    }, 
    { 'bar': '', 
     'baz': '', 
     'eee': '' 
    }  
    ] 
} 
+1

我沒有看到一個問題。您是否遇到以下問題:https://golang.org/doc/faq#convert_slice_of_interface? – JimB 2015-04-01 20:26:10

+0

是的,請給出關於您遇到的問題的更多細節。 – robbrit 2015-04-01 20:37:57

+0

以粗體更新。我正在尋找將類型[] OtherType或任何其他我決定創建的新結構移交給UnmarshalStruct,並讓它返回給我那個相同的結構,只是充滿了數據 – user2402831 2015-04-01 20:52:00

回答

1

真的沒有辦法做到你想要的而沒有反思。我個人對此有不同的結構,因此,您可以解組爲更通用的類型,如map[string]string或@ThunderCat所示,將中間狀態和解密狀態直接刪除到正確的類型中。不過,這是可以做到...

(我感動json.RawMessage直接進入TestStruct擺脫一層間接的,使這個例子更清楚)

type TestStruct struct { 
    Slice []json.RawMessage 
} 

func (t TestStruct) UnmarshalStruct(v interface{}) error { 
    // get the a Value for the underlying slice 
    slice := reflect.ValueOf(v).Elem() 
    // make sure we have adequate capacity 
    slice.Set(reflect.MakeSlice(slice.Type(), len(t.Slice), len(t.Slice))) 

    for i, val := range t.Slice { 
     err := json.Unmarshal(val, slice.Index(i).Addr().Interface()) 
     if err != nil { 
      return err 
     } 
    } 

    return nil 
} 

然後你可以把它像所以

var others []OtherType 
err := ts.UnmarshalStruct(&others) 
if err != nil { 
    log.Fatal(err) 
} 

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

+0

這是完美的,正是我一直在尋找的。我希望這對於我的特定架構限制儘可能抽象,也許我從另一個角度來看這是奇怪的,但這是現實。非常感謝。 – user2402831 2015-04-02 16:16:42

1

如果我理解正確的話,你要解組數據對兩種類型的片:

type A struct { 
    Foo string `json:"foo"` 
    ID string `json:"id"` 
} 

type B struct { 
    Bar string `json:"bar"` 
    Baz string `json:"baz"` 
    Eee string `json:"eee"` 
} 
SearchHit Source

的JSON包可以完成大部分的工作對你:

func executeQuery(q Query, v interface{}) error { 
    // Get a SearchHit. I am making this up. 
    // I have no idea how the package works. 
    searchHit, err := getHit(q) 
    if err != nil { 
     return err 
    } 
    // This is the important part. Convert the raw message to 
    // a slice of bytes and decode to the caller's slice. 
    return json.Unmarshal([]byte(*searchHit.Source), v) 
} 

你可以調用這個函數來進行解碼的各類切片或指針的類型片。

// Slice of type 
var s1 []TypeA 
if err := executeQuery(q1, &s1); err != nil { 
    // handle error 
} 

// Slice of pointer to type 
var s2 []*TypeB 
if err := error(q2, &s2); err != nil { 
    // handle error 
} 

我知道這不是問題的直接答案,但是這是通常處理這種情況的方式。

0

我不認爲這很容易做到。在Godoc中的Raw Message Example中,他們使用json中的值(在他們的示例中爲「Space」)來確定要解組到哪個結構類型。

爲了這個工作,函數必須有一些方法來獲取程序中定義的每個結構,然後它必須檢查每個json對象並使用反射將其與每個結構進行比較最有可能的是哪種類型。如果有多個結構「可以做到」呢?然後解決衝突使事情複雜化。

總之,我認爲你不能這樣做。