簡體   English   中英

將 map[string]DynamoDBAttributeValue 解組為結構

[英]Unmarshal map[string]DynamoDBAttributeValue into a struct

我正在嘗試使用aws-sdk-go設置一個 AWS-lambda,每當將新user添加到某個dynamodb表時就會觸發它。

一切正常,但我找不到解組 map map[string]DynamoDBAttributeValue的方法,例如:

{
    "name": {
        "S" : "John"
    },
    "residence_address": {
        "M": {
            "address": {
                "S": "some place"
            }
        }
    }
}

對於給定的結構,例如, User結構。 這里顯示了一個將map[string]*dynamodb.AttributeValue到給定接口的示例,但我找不到用map[string]DynamoDBAttributeValue做同樣事情的方法,即使這些類型似乎符合相同的目的.

map[string]DynamoDBAttributeValueevents.DynamoDBEvents從 package 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屬性。

或者我什至可以編寫一個 function 將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上這將是一個非常棘手的過程。

有沒有一種方法可以將來自 events.DynamoDBEvent 的events.DynamoDBEvent記錄解組到結構中,並使用與map[string]*dynamodb.AttributeValue所示的類似方法?

我嘗試了你提供的功能,我遇到了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
}

但我仍然對其他實現持開放態度。

我很沮喪,記錄中的NewImage類型不是map [string] * dynamodb.AttributeValue所以我可以使用dynamodbattribute包。

events.DynamoDBAttributeValue的JSON表示似乎與dynamodb.AttributeValue的JSON表示相同。

所以我嘗試創建自己的DynamoDBEvent類型並更改了OldImage和NewImage的類型,因此它將被編組到map [string] * dynamodb.AttributeValue而不是map [string] events.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"`
}

我發現了同樣的問題,解決方案是執行簡單的類型轉換。 這是可能的,因為最終 lambda 事件events.DynamoDBAttributeValue接收到的類型與 AWS DynamoDB types.AttributeValue的 SDK V2 使用的類型是相同的。 接下來我給大家展示一下轉換代碼。

package aws_lambda

import (
    "github.com/aws/aws-lambda-go/events"
    "github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue"
    "github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
)

func UnmarshalDynamoEventsMap(
    record map[string]events.DynamoDBAttributeValue, out interface{}) error {
    asTypesMap := DynamoDbEventsMapToTypesMap(record)
    err := attributevalue.UnmarshalMap(asTypesMap, out)
    if err != nil {
        return err
    }
    return nil
}

func DynamoDbEventsMapToTypesMap(
    record map[string]events.DynamoDBAttributeValue) map[string]types.AttributeValue {
    resultMap := make(map[string]types.AttributeValue)
    for key, rec := range record {
        resultMap[key] = DynamoDbEventsToTypes(rec)
    }
    return resultMap
}

// DynamoDbEventsToTypes relates the dynamo event received by AWS Lambda with the data type that is
// used in the Amazon SDK V2 to deal with DynamoDB data.
// This function is necessary because Amazon does not provide any kind of solution to make this
// relationship between types of data.
func DynamoDbEventsToTypes(record events.DynamoDBAttributeValue) types.AttributeValue {
    var val types.AttributeValue

    switch record.DataType() {
    case events.DataTypeBinary:
        val = &types.AttributeValueMemberB{
            Value: record.Binary(),
        }
    case events.DataTypeBinarySet:
        val = &types.AttributeValueMemberBS{
            Value: record.BinarySet(),
        }
    case events.DataTypeBoolean:
        val = &types.AttributeValueMemberBOOL{
            Value: record.Boolean(),
        }
    case events.DataTypeList:
        var items []types.AttributeValue
        for _, value := range record.List() {
            items = append(items, DynamoDbEventsToTypes(value))
        }
        val = &types.AttributeValueMemberL{
            Value: items,
        }
    case events.DataTypeMap:
        items := make(map[string]types.AttributeValue)
        for k, v := range record.Map() {
            items[k] = DynamoDbEventsToTypes(v)
        }
        val = &types.AttributeValueMemberM{
            Value: items,
        }
    case events.DataTypeNull:
        val = nil
    case events.DataTypeNumber:
        val = &types.AttributeValueMemberN{
            Value: record.Number(),
        }
    case events.DataTypeNumberSet:
        val = &types.AttributeValueMemberNS{
            Value: record.NumberSet(),
        }
    case events.DataTypeString:
        val = &types.AttributeValueMemberS{
            Value: record.String(),
        }
    case events.DataTypeStringSet:
        val = &types.AttributeValueMemberSS{
            Value: record.StringSet(),
        }
    }

    return val
}

有一個 package 允許從 events.DynamoDBAttributeValue 到 dynamodb.AttributeValue 的轉換

https://pkg.go.dev/github.com/aereal/go-dynamodb-attribute-conversions/v2

從那里可以將 AttributeValue 解組為結構

func Unmarshal(attribute map[string]events.DynamoDBAttributeValue, out interface{}) error {

    av := ddbconversions.AttributeValueMapFrom(attribute)

    return attributevalue.UnmarshalMap(av, out)
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM