Golang抽象出了Interface Slice转换

时间:2015-04-01 20:23:11

标签: go

我有一个对象列表(确切地说是olievere / Elastic SearchResult.Hits)。它们中的每一个都有一个json.RawMessage对象,我希望创建一个可抽象的方法,它接受任何结构的接口切片,Unmarshal的每个单独命中' json.RawMessage进入所述结构体,并将其附加到传入的[]interface中。

这个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': ''
    }    
  ]
}

3 个答案:

答案 0 :(得分:3)

如果没有反思,真的没办法做你想做的事。我个人会以不同的方式构建它,以便您解组为更通用的类型,如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

答案 1 :(得分: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
}

我知道这不是问题的直接答案,但这就是通常如何处理这种情况。

答案 2 :(得分:0)

我不相信这很容易做到。在godocs的Raw Message Example中,他们在json中使用了一个值," Space"在他们的例子中,确定要解组的结构类型。

为了使这个工作,函数必须有一些方法来获取为程序定义的每个结构,然后它必须检查每个json对象并使用反射将其与每个结构进行比较以找出它最有可能是哪种类型。如果有多个结构可以成为"那该怎么办?然后冲突解决使事情复杂化。

简而言之,我认为你无法做到这一点。