0

(Note, Dgraph isn't required for this problem; it's just the tool I am using where I am encountering the problem)

Let's say you have some structs

type AccountSettings struct {
    Uid      string `json:"uid"`
    DType    string `json:"dgraph.type"`
    Name     string `json:"name"`
    Username string `json:"username"`
}

type Settings struct {
    Uid     string          `json:"uid"`
    DType   string          `json:"dgraph.type"`
    Account AccountSettings `json:"account"`
}

type Tag struct {
    Uid   string `json:"uid"`
    DType string `json:"dgraph.type"`
    Label Label  `json:"label"`
}

type User struct {
    Uid      string   `json:"uid"`
    DType    string   `json:"dgraph.type"`
    Settings Settings `json:"settings"`
    Tags     []Tag    `json:"tags"`
}

and you create a user and add it to your Dgraph database

user = User{
    DType: "User",
    Settings: Settings{
        DType: "Settings",
        Account: SettingsAccount{
            DType: "SettingsAccount",
            Name: "Billy Bob"
            Username: "billyboy123"
        },
        Tags: []Tag{{
            DType: "Tag",
            Label: "admin"
        }, {
            DType: "Tag",
            Label: "user"
        }}
    },
}

And then you query your Dgraph database like (assuming you have the uid of the user node)

{
  user(func: uid("0x1234")) {
    uid
    settings {
      account {
        name
        username
      }
    }
    tags {
      label
    }
  }
}

the response will come back as

{
  "data": {
    "user": [
      {
        "uid": "0x1234"
        "settings": [
          {
            "account": [
              {
                "name": "Billy Bob",
                "username": "billyboy123"
              }
            ]
          }
        ]
        "tags": [
          {
            label: "admin"
          },
          {
            label: "user"
          },
        ]
      }
    ]
  }
}

So the problem is that the JSON response of Dgraph will return fields of struct types as arrays (presumably because there could potentially be multiple nodes pointing to a single other node), but since it's an array, you can't immediately marshal it with the User struct (because User.Settings is of type Settings, not []Settings).

What would you do in order to marshal the Dgraph JSON response with the User struct (or any other kind of struct)?

It's important to note that the settings array in the JSON response should just be the first element in that array, but the tags array should still remain an array because that's what's specified in the User struct.

sdfsdf
  • 5,052
  • 9
  • 42
  • 75

1 Answers1

2

You can do this with a custom Unmarshal as described here:

http://choly.ca/post/go-json-marshalling/

You use an alias by embedding the original User in an anonymous type that has a slice of Settings inside it that you Umarshal into like so:

func (u *User) UnmarshalJSON(data []byte) error {
    type Alias User
    aux := &struct {
        Settings []*Settings `json:"settings"`
        *Alias
    }{
        Alias: (*Alias)(u),
    }
    if err := json.Unmarshal(data, &aux); err != nil {
        return err
    }
    u.Settings = aux.Settings[0]
    return nil
}

A full working example is here (you need the same trick for the AccountSettings):

https://play.golang.org/p/FQWakX8B7OF

Iain Duncan
  • 3,139
  • 2
  • 17
  • 28