Skip to content

DefaultStringConverterProvider can't convert Map with Enum as a key #2102

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
bbednarek opened this issue Oct 20, 2020 · 4 comments
Open

DefaultStringConverterProvider can't convert Map with Enum as a key #2102

bbednarek opened this issue Oct 20, 2020 · 4 comments
Labels
dynamodb-enhanced feature-request A feature should be added or improved. p3 This is a minor priority issue

Comments

@bbednarek
Copy link

Mapping of the Map<K,String> where K is enum doesn't work.

Example entity:

@DynamoDbBean
@Data
public class TestClass {

    @DynamoDbPartitionKey
    private String id;
    private Map<Status, String> map;
   
    public enum Status {
         ONE, TWO
     }
}

Example TableSchema

private TableSchema<TestClass> tableSchema = TableSchema.fromBean(TestClass.class);

Describe the bug

Mapping doesn't work.

Expected Behavior

Mapping works.

Current Behavior

It fails with the following error:

Caused by: java.lang.IllegalArgumentException: No string converter exists for class test.TestClass.Status

Full stacktrace:

Caused by: java.lang.IllegalArgumentException: No string converter exists for class test.TestClass.Status
	at software.amazon.awssdk.enhanced.dynamodb.internal.converter.string.DefaultStringConverterProvider.converterFor(DefaultStringConverterProvider.java:155)
	at software.amazon.awssdk.enhanced.dynamodb.DefaultAttributeConverterProvider.createMapConverter(DefaultAttributeConverterProvider.java:182)
	at software.amazon.awssdk.enhanced.dynamodb.DefaultAttributeConverterProvider.findConverter(DefaultAttributeConverterProvider.java:149)
	at software.amazon.awssdk.enhanced.dynamodb.DefaultAttributeConverterProvider.converterFor(DefaultAttributeConverterProvider.java:133)
	at software.amazon.awssdk.enhanced.dynamodb.mapper.ImmutableAttribute.converterFrom(ImmutableAttribute.java:167)
	at software.amazon.awssdk.enhanced.dynamodb.mapper.ImmutableAttribute.resolve(ImmutableAttribute.java:163)
	at software.amazon.awssdk.enhanced.dynamodb.mapper.StaticImmutableTableSchema.lambda$new$0(StaticImmutableTableSchema.java:153)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
	at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1624)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
	at java.base/java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:312)
	at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:734)
	at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:658)
	at software.amazon.awssdk.enhanced.dynamodb.mapper.StaticImmutableTableSchema.<init>(StaticImmutableTableSchema.java:159)
	at software.amazon.awssdk.enhanced.dynamodb.mapper.StaticImmutableTableSchema.<init>(StaticImmutableTableSchema.java:77)
	at software.amazon.awssdk.enhanced.dynamodb.mapper.StaticImmutableTableSchema$Builder.build(StaticImmutableTableSchema.java:425)
	at software.amazon.awssdk.enhanced.dynamodb.mapper.StaticTableSchema.<init>(StaticTableSchema.java:66)
	at software.amazon.awssdk.enhanced.dynamodb.mapper.StaticTableSchema.<init>(StaticTableSchema.java:64)
	at software.amazon.awssdk.enhanced.dynamodb.mapper.StaticTableSchema$Builder.build(StaticTableSchema.java:255)
	at software.amazon.awssdk.enhanced.dynamodb.mapper.BeanTableSchema.createStaticTableSchema(BeanTableSchema.java:200)
	at software.amazon.awssdk.enhanced.dynamodb.mapper.BeanTableSchema.create(BeanTableSchema.java:124)
	at software.amazon.awssdk.enhanced.dynamodb.mapper.BeanTableSchema.create(BeanTableSchema.java:116)
	at software.amazon.awssdk.enhanced.dynamodb.TableSchema.fromBean(TableSchema.java:80)
	at test.DynamoDbTestConfiguration.<init>(DynamoDbTestConfiguration.java:22)
	at test.DynamoDbTestConfiguration$$EnhancerBySpringCGLIB$$9f051cd1.<init>(<generated>)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:500)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:481)
	at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:204)
	... 111 more

If I swap Map key and value:

@DynamoDbBean
@Data
public class TestClass {

    @DynamoDbPartitionKey
    private String id;
    private Map<String, Status> map;
   
    public enum Status {
         ONE, TWO
     }
}

it works!

Steps to Reproduce

Follow the steps above.

Possible Solution

Support Enum as a key in a Map.

Context

I am trying to save a map with enum as key.

Your Environment

  • AWS Java SDK version used: 2.15.2
  • JDK version used: 14
  • Operating System and version: MacOS 10.15.7

Related to the comment #35 (comment)

@bbednarek bbednarek added bug This issue is a bug. needs-triage This issue or PR still needs to be triaged. labels Oct 20, 2020
@debora-ito
Copy link
Member

@bbednarek the default MapAttributeConverter only supports StringAttributeConverter for keys: https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/internal/converter/attribute/MapAttributeConverter.html

I'm marking this as a feature request. In the meantime to get unblocked you can write a custom Attribute converter, check the DynamoDB Enhanced Client Overview for examples: https://github.com/aws/aws-sdk-java-v2/tree/master/services-custom/dynamodb-enhanced#control-attribute-conversion

@debora-ito debora-ito added dynamodb-enhanced feature-request A feature should be added or improved. and removed bug This issue is a bug. needs-triage This issue or PR still needs to be triaged. labels Oct 22, 2020
@antukhov
Copy link

antukhov commented Jan 6, 2022

Recently solved a similar task using the official dynamodb-enhanced client which contains DynamoDbConvertedBy annotation. There you can specify a converter class that implements the AttributeConverter interface for your data type.

Below is just my example of converter implementation for Map<EnumClass, String> type:

@DynamoDbConvertedBy(EnumMapAttributeConverter.class) for the entity field's getter.

Class itself:

public class EnumMapAttributeConverter
    implements AttributeConverter<Map<EnumClass, String>> {

  @Override
  public AttributeValue transformFrom(final Map<EnumClass, String> input) {
    Map<String, AttributeValue> attributeValueMap =
        input.entrySet().stream()
            .collect(
                Collectors.toMap(
                    k -> k.getKey().getValue(),
                    v -> AttributeValue.builder().s(v.getValue()).build()));
    return AttributeValue.builder().m(attributeValueMap).build();
  }

  @Override
  public Map<EnumClass, String> transformTo(final AttributeValue input) {
    return input.m().entrySet().stream()
        .collect(
            Collectors.toMap(
                k -> getEnumClassKeyByString(k.getKey()), v -> v.getValue().s()));
  }

  private EnumClass getEnumClassKeyByString(final String key) {
    EnumClass enumClass = EnumClass.getByValue(key);
    return enumClass != null ? enumClass : EnumClass.NOT_FOUND;
  }

  @Override
  public EnhancedType<Map<EnumClass, String>> type() {
    return EnhancedType.mapOf(EnumClass.class, String.class);
  }

  @Override
  public AttributeValueType attributeValueType() {
    return AttributeValueType.M;
  }
}

aws-sdk-java-automation added a commit that referenced this issue Jul 21, 2022
…4aba387b9

Pull request: release <- staging/99eafe6d-0258-434f-81e0-a5b4aba387b9
@hamburml
Copy link

hamburml commented Nov 3, 2022

@antukhov Can you provide a working example pls? I added an AttributeConverterClass, Getter-Method and the Annotation - still the same IllegalArgument No string converter exists ... exception. Thanks!

@yasminetalby yasminetalby added the p3 This is a minor priority issue label Nov 28, 2022
@ashr123
Copy link

ashr123 commented Apr 1, 2024

@antukhov I did something similar just for complicated map value which is also annotated with DynamoDbBean:

public class EnumMapAttributeConverter implements AttributeConverter<Map<MVDayOfWeek, SeoLineFrequency>> {
    private static final BeanTableSchema<SeoLineFrequency> seoLineFrequencyBeanTableSchema = TableSchema.fromBean(SeoLineFrequency.class);

    @Override
    public AttributeValue transformFrom(Map<MVDayOfWeek, SeoLineFrequency> input) {
        return AttributeValue.fromM(input.entrySet().stream()
                .collect(Collectors.toMap(
                       entry -> entry.getKey().name(),
                       entry -> AttributeValue.fromM(seoLineFrequencyBeanTableSchema.itemToMap(entry.getValue(), true))
                )));
    }

    @Override
    public Map<MVDayOfWeek, SeoLineFrequency> transformTo(AttributeValue input) {
        return input.m().entrySet().stream()
                .collect(Collectors.toMap(
                        entry -> MVDayOfWeek.valueOf(entry.getKey()),
                        entry -> seoLineFrequencyBeanTableSchema.mapToItem(entry.getValue().m())
                ));
    }

    @Override
    public EnhancedType<Map<MVDayOfWeek, SeoLineFrequency>> type() {
        return EnhancedType.mapOf(MVDayOfWeek.class, SeoLineFrequency.class);
    }

    @Override
    public AttributeValueType attributeValueType() {
        return AttributeValueType.M;
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
dynamodb-enhanced feature-request A feature should be added or improved. p3 This is a minor priority issue
Projects
None yet
Development

No branches or pull requests

6 participants