英文:
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种类型(int64
、string
和float64
),但你可以通过添加新的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 "Objects"
.
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 <-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
}
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), "strval", 32.2}}
key, err := datastore.Put(c, datastore.NewIncompleteKey(c, "MyMy", nil), &m)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论