google-app-enginegoserializationdatastoregoogle-app-engine-go

How to implement google datastore propertyloadsaver with arrays of structs


How do you implement Load() and Save() for googles datastore when you have an array of structs? It is clearly possible, but how?

Firstly, when you allow the datastore itself to serialise a Person with a list of Phone objects, you can use reflection to see it internally creates a list of *datastore.Entity objects:

package main

import (
    "fmt"
    "reflect"

    "cloud.google.com/go/datastore"
)

type Phone struct {
    Type   string
    Number string
}

type Person struct {
    Name  string
    Phone []Phone
}

func main() {
    person := Person{Name: "Bob", Phone: []Phone{Phone{"a", "b"}, Phone{"c", "d"}}}

    // save here

    }
}

Here is my work so far, this saves the name field, but causes an error on the *datastore.Entity objects. here is my attempt:

func (p *Person) Save() ([]datastore.Property, error) {
    props := []datastore.Property{
        {
            Name:  "Name",
            Value: p.Name,
        },
    }

    var n []*datastore.Entity
    for _, x := range p.Phone {
        i1 := datastore.Property{Name: "Type", Value: x.Type}
        i2 := datastore.Property{Name: "Number", Value: x.Number}
        e := &datastore.Entity{Properties: []datastore.Property{i1, i2}}
        n = append(n, e)
    }
    props = append(props, datastore.Property{Name:"Phone",Value:n})

    return props, nil
}

Datastore itself complains with the following error:

invalid Value type []*datastore.Entity for a Property with Name "Phone"

I wonder if anyone can elucidate where I am going wrong? How do I save an array of structs in datastore in the same way datastore itself does?

Sample code above is on go playground: https://play.golang.org/p/AP1oFnlo1jm


Solution

  • After some amount of experimentation it turns out here is how to implement a Save() method for an object that has structs. The datastore.Property must store an array of []interface{} holding []*datastore.Entity rather than a pure []*datastore.Entity:

    func (p *Person) Save() ([]datastore.Property, error) {
        props := []datastore.Property{
            {
                Name:  "Name",
                Value: p.Name,
            },
        }
    
        var n []interface{}
        for _, x := range p.Phone {
            i1 := datastore.Property{Name: "Type", Value: x.Type}
            i2 := datastore.Property{Name: "Number", Value: x.Number}
            e := &datastore.Entity{Properties: []datastore.Property{i1, i2}}
            n = append(n, e)
        }
        props = append(props, datastore.Property{Name:"Phone",Value:n})
    
        return props, nil
    }