Unmarshal map [string]DynamoDBAttributeValue в структуру
Я пытаюсь настроить AWS-lambda с помощью aws-sdk-go, который запускается всякий раз, когда новый user
добавляется в определенную таблицу dynamodb.
Все работает просто отлично, но я не могу найти способ разомкнуть карту map[string]DynamoDBAttributeValue
, например:
{
"name": {
"S" : "John"
},
"residence_address": {
"M": {
"address": {
"S": "some place"
}
}
}
}
К данной структуре, например, к структуре User
. Здесь показан пример unmarhaling a map[string]*dynamodb.AttributeValue
в данный интерфейс, но я не могу найти способ сделать то же самое с map[string]DynamoDBAttributeValue
, хотя эти типы похоже, они подходят для тех же целей.
map[string]DynamoDBAttributeValue
возвращается a events.DynamoDBEvents
из пакета github.com/aws/aws-lambda-go/events
. Вот мой код:
package handler
import (
"context"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
"github.com/aws/aws-sdk-go/service/dynamodb"
)
func HandleDynamoDBRequest(ctx context.Context, e events.DynamoDBEvent) {
for _, record := range e.Records {
if record.EventName == "INSERT" {
// User Struct
var dynamoUser model.DynamoDBUser
// Of course this can't be done for incompatible types
_ := dynamodbattribute.UnmarshalMap(record.Change.NewImage, &dynamoUser)
}
}
}
Конечно, я могу маршалировать record.Change.NewImage
в JSON и разархивировать его обратно в заданную структуру, но тогда мне придется вручную инициализировать атрибуты dynamoUser
, начиная с последних.
Или я мог бы даже написать функцию, которая разбирает map[string]DynamoDBAttributeValue
на map[string]*dynamodb.AttributeValue
, например:
func getAttributeValueMapFromDynamoDBStreamRecord(e events.DynamoDBStreamRecord) map[string]*dynamodb.AttributeValue {
image := e.NewImage
m := make(map[string]*dynamodb.AttributeValue)
for k, v := range image {
if v.DataType() == events.DataTypeString {
s := v.String()
m[k] = &dynamodb.AttributeValue{
S : &s,
}
}
if v.DataType() == events.DataTypeBoolean {
b := v.Boolean()
m[k] = &dynamodb.AttributeValue{
BOOL : &b,
}
}
// . . .
if v.DataType() == events.DataTypeMap {
// ?
}
}
return m
}
А затем просто использовать dynamodbattribute.UnmarshalMap
, но на events.DataTypeMap
это было бы довольно сложно процесс.
Есть ли способ, с помощью которого я могу разомкнуть запись DynamoDB, поступающую из events.DynamoDBEvent
в структуру с помощью аналогичного метода, показанного для map[string]*dynamodb.AttributeValue
?
3 ответа:
Я попробовал функцию, которую вы предоставили, и столкнулся с некоторыми проблемами с
events.DataTypeList
, поэтому мне удалось написать следующую функцию, которая делает трюк:// UnmarshalStreamImage converts events.DynamoDBAttributeValue to struct func UnmarshalStreamImage(attribute map[string]events.DynamoDBAttributeValue, out interface{}) error { dbAttrMap := make(map[string]*dynamodb.AttributeValue) for k, v := range attribute { var dbAttr dynamodb.AttributeValue bytes, marshalErr := v.MarshalJSON(); if marshalErr != nil { return marshalErr } json.Unmarshal(bytes, &dbAttr) dbAttrMap[k] = &dbAttr } return dynamodbattribute.UnmarshalMap(dbAttrMap, out) }
На данный момент мне удалось решить эту проблему, реализовав функцию, которую я упомянул в вопросе.
Но я все еще открыт для других реализаций.// May take either `map[string]DynamoDBAttributeValue` // or `DynamoDBAttributeValue` as input. func EventStreamToMap(attribute interface{}) map[string]*dynamodb.AttributeValue { // Map to be returned m := make(map[string]*dynamodb.AttributeValue) tmp := make(map[string]events.DynamoDBAttributeValue) switch t := attribute.(type) { case map[string]events.DynamoDBAttributeValue: tmp = t case events.DynamoDBAttributeValue: tmp = t.Map() } for k, v := range tmp { switch v.DataType() { case events.DataTypeString: s := v.String() m[k] = &dynamodb.AttributeValue{ S : &s, } case events.DataTypeBoolean: b := v.Boolean() m[k] = &dynamodb.AttributeValue{ BOOL : &b, } // case events.SomeOtherType: // ... case events.DataTypeMap: m[k] = &dynamodb.AttributeValue{ M : EventStreamToMap(v), } } } return m }
Я был разочарован тем, что тип нового изображения из записи не был map [string] * dynamodb.AttributeValue, чтобы я мог использовать пакет dynamodbattribute.
Представление событий в формате JSON.DynamoDBAttributeValue, по-видимому, совпадает с JSON-представлением dynamodb.Атрибутивное значение.
Поэтому я попытался создать свой собственный тип DynamoDBEvent и изменил тип OldImage и NewImage, чтобы он был маршаллирован в map [string] * dynamodb.AttributeValue вместо отображение событий [string].DynamoDBAttributeValue
Это немного некрасиво, но это работает для меня.package main import ( "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" "github.com/aws/aws-sdk-go/service/dynamodb" "github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute" "fmt" ) func main() { lambda.Start(lambdaHandler) } // changed type of event from: events.DynamoDBEvent to DynamoDBEvent (see below) func lambdaHandler(event DynamoDBEvent) error { for _, record := range event.Records { change := record.Change newImage := change.NewImage // now of type: map[string]*dynamodb.AttributeValue var item IdOnly err := dynamodbattribute.UnmarshalMap(newImage, &item) if err != nil { return err } fmt.Println(item.Id) } return nil } type IdOnly struct { Id string `json:"id"` } type DynamoDBEvent struct { Records []DynamoDBEventRecord `json:"Records"` } type DynamoDBEventRecord struct { AWSRegion string `json:"awsRegion"` Change DynamoDBStreamRecord `json:"dynamodb"` EventID string `json:"eventID"` EventName string `json:"eventName"` EventSource string `json:"eventSource"` EventVersion string `json:"eventVersion"` EventSourceArn string `json:"eventSourceARN"` UserIdentity *events.DynamoDBUserIdentity `json:"userIdentity,omitempty"` } type DynamoDBStreamRecord struct { ApproximateCreationDateTime events.SecondsEpochTime `json:"ApproximateCreationDateTime,omitempty"` // changed to map[string]*dynamodb.AttributeValue Keys map[string]*dynamodb.AttributeValue `json:"Keys,omitempty"` // changed to map[string]*dynamodb.AttributeValue NewImage map[string]*dynamodb.AttributeValue `json:"NewImage,omitempty"` // changed to map[string]*dynamodb.AttributeValue OldImage map[string]*dynamodb.AttributeValue `json:"OldImage,omitempty"` SequenceNumber string `json:"SequenceNumber"` SizeBytes int64 `json:"SizeBytes"` StreamViewType string `json:"StreamViewType"` }