amazon-web-serviceskotlinamazon-dynamodbmoshi

Android AWS DynamoDB: Using AWS mapper.save with Moshi


I use an AWS DynamoDB database to store data. I'm saving a "CommunityUser" object, which class looks as follows:

class CommunityUser
{
    @get:DynamoDBHashKey(attributeName = AWSTables.TableCommunityUser.user_id)
    @Json(name = "user_id")
    var userId: String = ""

    @get:DynamoDBAttribute(attributeName = AWSTables.TableCommunityUser.email)
    @Json(name = "email")
    var email: String = ""

    @get:DynamoDBAttribute(attributeName = AWSTables.TableCommunityUser.user)
    @Json(name = "user")
    var alias: String = ""

    @get:DynamoDBAttribute(attributeName = AWSTables.TableCommunityUser.pass)
    @Json(name = "pass")
    var password: String = ""

    @get:DynamoDBAttribute(attributeName = AWSTables.TableCommunityUser.picture)
    @Json(name = "picture")
    var picture: String = ""
}

The class properties names do not match with AWS table fields names, so I was successfully using GSON SerializedName decorator to set the mapping like so:

class CommunityUser
{
    @get:DynamoDBHashKey(attributeName = AWSTables.TableCommunityUser.user_id)
    @SerializedName(value = "user_id")
    var userId: String = ""

    @get:DynamoDBAttribute(attributeName = AWSTables.TableCommunityUser.email)
    @SerializedName(value = "email")
    var email: String = ""

    @get:DynamoDBAttribute(attributeName = AWSTables.TableCommunityUser.user)
    @Json(name = "user")
    var alias: String = ""

    @get:DynamoDBAttribute(attributeName = AWSTables.TableCommunityUser.pass)
    @SerializedName(value = "pass")
    var password: String = ""

    @get:DynamoDBAttribute(attributeName = AWSTables.TableCommunityUser.picture)
    @SerializedName(value = "picture")
    var picture: String = ""
}

But now I'm migrating from Gson to Moshi, and that's why in the first piece of code you see Json decorator instead of SerializedName, but the mapping is not working with moshi and on the contrary it's crashing.

This is where I try -for example- to update a user:

override fun updateUser(jsonUser: String): CommunityUserDTO? {
    val user: CommunityUser?
    var userDTO: CommunityUserDTO? = null
    val dynamoDBMapperConfig = AWSConnection.mapperConfig4Update
    try {
        user = AWSHelper.deserializeUser(jsonUser)
        AWSTestRepository.awsConnection.mapper.save(user, dynamoDBMapperConfig)
        userDTO = jsonUser.deserialize<CommunityUserDTO>()
    } catch (e: Exception) {
        e.logException()
    }
    return userDTO
}

And mapper.save is crashing with the following exception:

Null or empty value for key: public final java.lang.String core.model.CommunityUser.getUserId()

Maybe I need to set up DynamoDB to work with Moshi? Not sure, but I need help to use Moshi with DynamoDB.

This is the class where I initialize AWS Mapper and connection:

class AWSConnection {
    lateinit var mapper: DynamoDBMapper
        private set
    lateinit var ddbClient: AmazonDynamoDBClient
        private set

    companion object {
        private lateinit var poolId: String
        private val databaseRegion = Regions.xxx
        @JvmStatic
        val mapperConfig4Update: DynamoDBMapperConfig
            get() = DynamoDBMapperConfig.Builder()
                .withConsistentReads(DynamoDBMapperConfig.ConsistentReads.CONSISTENT)
                .withSaveBehavior(DynamoDBMapperConfig.SaveBehavior.UPDATE)
                .build()

        // initialize connection
        private fun initializeAWSIdentityPoolDynamoDBMapper(context: Context): DynamoDBMapper {
            val credentialsProvider = CognitoCachingCredentialsProvider(
                context,
                poolId,
                databaseRegion
            )
            val ddbClient = com.amazonaws.regions.Region.getRegion(databaseRegion)
                .createClient(
                    AmazonDynamoDBClient::class.java,
                    credentialsProvider,
                    ClientConfiguration()
                )
            return DynamoDBMapper.builder().dynamoDBClient(ddbClient).build()
        }

        private fun initializeAWSIdentityPoolAmazonDynamoDBClient(context: Context): AmazonDynamoDBClient {
            val credentialsProvider = CognitoCachingCredentialsProvider(
                context,
                poolId,
                databaseRegion
            )
            return com.amazonaws.regions.Region.getRegion(databaseRegion)
                .createClient(
                    AmazonDynamoDBClient::class.java,
                    credentialsProvider,
                    ClientConfiguration()
                )
        }
    }
}

Solution

  • Gosh!! It was just a silly mistake!!

    In "CommunityUser" class (the one in my case DynamoDB uses to map with database) I forgot the Moshi's @JsonClass(generateAdapter = true) decorator.

    I'm using a custom Moshi adapter, which is faster than the built-in, so that decorator is needed in every class you will serialize/deserialize at any time.