【问题标题】:Golang abstracted Interface Slice conversionGolang抽象的Interface Slice转换
【发布时间】:2015-04-01 20:23:10
【问题描述】:

我有一个对象列表(确切地说是olievere/Elastic SearchResult.Hits)。其中每一个都有一个json.RawMessage 对象,我正在寻找一个可抽象的方法,该方法接受任何结构的接口切片,Unmarshal 的每个人都将json.RawMessage 命中到所述结构中,并将其附加到传入的@987654324 @。

这个 func 不应该对所需的业务层结构有任何逻辑或洞察力,并且 DB Call 的接口非常繁重,因此对上述 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': ''
    }    
  ]
}

【问题讨论】:

  • 我没有看到任何问题。您是否有问题:golang.org/doc/faq#convert_slice_of_interface
  • 是的,请详细说明您遇到的问题。
  • 以粗体更新。我希望将 []OtherType 类型的东西或我决定创建的任何其他新结构交给 UnmarshalStruct,然后让它返回给我同样的结构,只是充满了数据
  • 已发布 json,但问题变得非常复杂,因为我觉得我在重复同样的事情。简单地说,我想要一个通用 func,我给一个实例化结构的接口切片(我们称之为 A)。然后它将填充 A,我可以从原始调用函数返回 A。
  • 所以问题是:给定一个异构 json 对象列表,我可以编写什么函数来神奇地将每个 json 对象解组为正确的结构类型,而无需事先告诉函数预期哪些结构类型?跨度>

标签: go


【解决方案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

【讨论】:

  • 这是完美的,正是我想要的。对于我的特定架构约束,我希望它尽可能抽象,也许我在另一种意义上奇怪地构建了它,但这是正确的。非常感谢。
【解决方案2】:

如果我理解正确,您希望将数据解组为两种类型的切片:

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
}

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

【讨论】:

    【解决方案3】:

    我不相信这很容易做到。在 godocs 的 Raw Message Example 中,他们使用 json 中的值(在他们的示例中为“Space”)来确定要解组到的结构类型。

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

    简而言之,我认为你做不到。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-08-06
      • 2017-10-17
      • 2014-12-30
      • 2019-09-22
      • 2017-01-17
      • 2022-01-24
      • 2018-11-10
      • 2020-12-31
      相关资源
      最近更新 更多