Custom DynamoDB Unmarshal into Golang Struct

benbpyle

Benjamen Pyle

Posted on March 8, 2023

Custom DynamoDB Unmarshal into Golang Struct

Short post on unmarshalling a DynamoDB Map into something strongly typed like a Go struct. If you want to jump straight to some code, here are the Github gists

So what is "unmarshalling"? It's the act of taking one representation of data and converting it into another. For instance when we store data in DynamoDB one of the native data types is a "map". A map is really nothing more than a dictionary/key value type look up. Think of a dictionary as a data structure that has a key and a subsequent value. The key must be unique. Take this data for example


{
  "Roles": {
    "1": {
      "name": "Role number 1",
      "id": 1
    },
    "2": {
      "name": "Role number 2",
      "id": 2
    }
  },
  "SK": "ROLE#1234",
  "PK": "USERPROFILE#1000130",
  "EntityType": "UserRole"
}
Enter fullscreen mode Exit fullscreen mode

The Roles property is a map. It contains keys of ["1"] and ["2"]. Then each of those keys contain another map inside of it. These look alot like JSON objects and for all intents and purposes, it's OK to think of them like that.

So with this data, how to do we get it into something that looks like this

type UserRole struct {
    EntityType string `dynamodbav:"EntityType"`
    Roles      []Role `dynamodbav:"Roles"`
}

type Role struct {
    Id   int
    Name string
}</pre>
Enter fullscreen mode Exit fullscreen mode

The UserRole struct has en EntityType property as well as a Roles slice that holds Role structs. Each field also has attributes that describe them as things that are represented in DyanamoDB Attribute values with names that correspond to the column names. Feel free to read up more here

To get started with the unmarshalling the first step is to build a UserRole and unmarshall it.


ur := &amp;UserRole{}
_ = dynamodbattribute.UnmarshalMap(i, ur)</pre>
Enter fullscreen mode Exit fullscreen mode

By default, this is going to really just unmarshal the fields that the Go library knows how to deal. So very similar to when working with JSON unmarshalling, the same can be done with DynamoDB

func (ur *UserRole) UnmarshalDynamoDBAttributeValue(value *dynamodb.AttributeValue) error {
    for k, kv := range value.M {
        if k == "EntityType" {
            ur.EntityType = *kv.S
        } else if k == "Roles" {
            for _, nkv := range kv.M {
                r := &amp;Role{}
                err := dynamodbattribute.UnmarshalMap(nkv.M, r)
                if err != nil {
                    return err
                }
                ur.Roles = append(ur.Roles, *r)
            }
        }
    }
    return nil
}
Enter fullscreen mode Exit fullscreen mode

Note that you can spin through the Key/Value pairs of the attribute value that is passed in. For each of the keys, you can determine what type of object is inside there. For instance the EntityType field is a string. So that's simple enough.

But with the Roles field, there is another map to deal with. So again, simply unmarshal that variable and take the custom step one layer further

func (r *Role) UnmarshalDynamoDBAttributeValue(value *dynamodb.AttributeValue) error {
    for k, kv := range value.M {
        if k == "id" {
            v, _ := strconv.ParseInt(*kv.N, 10, 32)
            r.Id = int(v)
        } else if k == "name" {
            r.Name = *kv.S
        }
    }

    return nil
}
Enter fullscreen mode Exit fullscreen mode

And there it is. An umarshalled custom type from a DynamoDB map. There isn't too much more too it but this super powerful as you can realize any type of data stored in DynamoDB into a Go struct with your custom code.

Hope this was helpful!

💖 💪 🙅 🚩
benbpyle
Benjamen Pyle

Posted on March 8, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related