在Google App Engine for Go中,一个属性如何具有多种类型的值?

huangapple go评论78阅读模式
英文:

In Google App Engine for Go, how can a property have values of more than one type?

问题

谷歌应用引擎(Google App Engine)针对Go语言的数据存储文档中提到:“一个属性可以有多个类型的值”。但是文档中没有给出示例或进一步的解释。(版本:appengine 1.9.19)

如果在支持的结构体中必须声明属性的特定类型,那么如何使属性具有多个类型呢?

英文:

The Google App Engine for Go datastore docs say, "
A property can have values of more than one type"
. There is no example or further explanation. (Version: appengine 1.9.19.)

How can a property have more than one type if you must declare that property with a specific type in the backing struct?

答案1

得分: 3

你不一定需要为后备结构中的属性声明特定的类型。

通过实现PropertyLoadSaver接口,你可以在加载或保存期间对实体的属性进行动态处理。参考这个答案,展示了如何在Go中将实体表示为通用的map[string]interface{}类型,以便具有动态属性。

回到你的问题:

属性可以具有多个类型的值。

这是正确的。但是,如果你想使其工作,你还需要通过PropertyLoadSaver接口利用自定义的加载/保存机制。

首先,定义一个后备的struct,其中具有多个不同类型值的属性可以是[]interface{}类型:

type MyMy struct {
    Objects []interface{}
}

接下来,我们需要实现PropertyLoadSaver接口。在加载时,我们只需将所有具有名称"Objects"的值追加到Objects切片中。

在保存时,我们将遍历Objects切片的元素,并将所有值与相同的属性名称一起发送。这将确保它们将保存在同一个属性下,并且我们还必须将Multiple字段指定为true(多值属性):

func (m *MyMy) Load(ch <-chan datastore.Property) error {
    for p := range ch { // Read until channel is closed
        if p.Name == "Objects" {
            m.Objects = append(m.Objects, p.Value)
        }
    }
    return nil
}

func (m *MyMy) Save(ch chan<- datastore.Property) error {
    defer close(ch)
    for _, v := range m.Objects {
        switch v2 := v.(type) {
        case int64: // Here v2 is of type int64
            ch <- datastore.Property{Name: "Objects", Value: v2, Multiple: true}
        case string:  // Here v2 is of type string
            ch <- datastore.Property{Name: "Objects", Value: v2, Multiple: true}
        case float64: // Here v2 is of type float64
            ch <- datastore.Property{Name: "Objects", Value: v2, Multiple: true}
        }
    }
    return nil
}

请注意,将类型为interface{}的值设置为Property.Value会导致错误,这就是为什么我使用了类型开关,以设置具体类型。在我的示例中,我只支持了3种类型(int64stringfloat64),但你可以通过添加新的case分支轻松添加更多类型。

使用它:

最后,使用我们自定义的MyMy类型保存一个具有多个不同类型值的属性为"Objects"的新实体:

m := MyMy{[]interface{}{int64(1234), "strval", 32.2}}
key, err := datastore.Put(c, datastore.NewIncompleteKey(c, "MyMy", nil), &m)
英文:

You do not necessarily have to declare a specific type for a property in a backing struct.

By implementing the PropertyLoadSaver interface, you can dynamically do whatever you want with the properties of an entity during loading or before saving. See this answer which shows how to represent an entity as a general map[string]interface{} type in Go, so it can have dynamic properties.

Back to your question:

> A property can have values of more than one type.

This is true. But if you want to make this work, you will also have to utilize a custom loading/saving mechanism through the PropertyLoadSaver interface.

First define a backing struct where the property which will have multiple values of different types may be an []interface{}:

type MyMy struct {
	Objects []interface{}
}

Next we have to implement PropertyLoadSaver. When loading, we will just append all values to the Objects slice that come with the name &quot;Objects&quot;.

When saving, we will loop over the elements of the Objects slice and send all its values with the same property name. This will ensure they will be saved under the same property, and we also have to specify the Multiple field to be true (multi-value property):

func (m *MyMy) Load(ch &lt;-chan datastore.Property) error {
	for p := range ch { // Read until channel is closed
		if p.Name == &quot;Objects&quot; {
			m.Objects = append(m.Objects, p.Value)
		}
	}
	return nil
}

func (m *MyMy) Save(ch chan&lt;- datastore.Property) error {
	defer close(ch)
	for _, v := range m.Objects {
		switch v2 := v.(type) {
		case int64: // Here v2 is of type int64
			ch &lt;- datastore.Property{Name: &quot;Objects&quot;, Value: v2, Multiple: true}
		case string:  // Here v2 is of type string
			ch &lt;- datastore.Property{Name: &quot;Objects&quot;, Value: v2, Multiple: true}
		case float64: // Here v2 is of type float64
			ch &lt;- datastore.Property{Name: &quot;Objects&quot;, Value: v2, Multiple: true}
		}
	}
	return nil
}

Note that setting a value of type interface{} as the Property.Value would result in an error, that is why I used a Type switch so I will set concrete types. In my example I only supported 3 types (int64, string and float64) but you can just as easily add more types by adding new case branches.

And using it:

And finally using our custom MyMy type to save a new entity with property "Objects" which will have multiple values and of different types:

m := MyMy{[]interface{}{int64(1234), &quot;strval&quot;, 32.2}}
key, err := datastore.Put(c, datastore.NewIncompleteKey(c, &quot;MyMy&quot;, nil), &amp;m)

huangapple
  • 本文由 发表于 2015年5月31日 05:41:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/30551659.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定