Skip to content
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

Is there any solution like ItemUtils.java in dynamodb aws-sdk-java v1 #1975

Closed
pkgonan opened this issue Aug 6, 2020 · 9 comments
Closed
Labels
dynamodb-enhanced feature-request A feature should be added or improved.

Comments

@pkgonan
Copy link

pkgonan commented Aug 6, 2020

Describe the Feature

I want to converter Map<String, AttributeValue> -> Map<String, Object>.

aws-sdk-java v1 provides ItemUtils.java for this feature, but v2 did not.
Is there any solution about that ?

[ItemUtils.java in v1]
https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/dynamodbv2/document/ItemUtils.html#toSimpleMapValue-java.util.Map-

Is your Feature Request related to a problem?

Many people needs this feature.

https://stackoverflow.com/questions/57280071/equivalent-of-itemutils-toattributevalue-in-dynamodb-jdk-2-x

awsdocs/aws-doc-sdk-examples#889

Describe alternatives you've considered

Changing database.

@pkgonan pkgonan added feature-request A feature should be added or improved. needs-triage This issue or PR still needs to be triaged. labels Aug 6, 2020
@debora-ito
Copy link
Member

There's no equivalent to ItemUtils in v2 because the way attributes are mapped in the DynamoDB Enhanced client has changed.

If you know the types you're working with, you can write an equivalent to ItemUtils using the DefaultAttributeConverterProvider.
E.g. if you want to convert a Map<String, Number>, you can do it with something like:

DefaultAttributeConverterProvider.create()
        .converterFor(EnhancedType.mapOf(String.class, Integer.class))
        .transformFrom(Collections.singletonMap("Key", 5))
// Returns AttributeValue(SS=[], NS=[], BS=[], M={Key=AttributeValue(N=5, SS=[], NS=[], BS=[], M={}, L=[])}, L=[])

If you need "dynamic" type discovery, you'd need to use a wrapper that dynamically selects the converter type.

@debora-ito debora-ito added dynamodb-enhanced response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 10 days. closing-soon This issue will close in 4 days unless further comments are made. and removed needs-triage This issue or PR still needs to be triaged. response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 10 days. labels Aug 7, 2020
@pkgonan
Copy link
Author

pkgonan commented Aug 8, 2020

@debora-ito
Thank you for your reply.

Is there any sample code about dynamic type discovery ?

If you need "dynamic" type discovery, you'd need to use a wrapper that dynamically selects the converter type.

@github-actions github-actions bot removed the closing-soon This issue will close in 4 days unless further comments are made. label Aug 8, 2020
@debora-ito
Copy link
Member

@pkgonan it would be something similar to the answer in the stackoverflow link you referenced in the description.

I'll go ahead and close this. Feel free to reach out if you have further questions.

@Sarev0k
Copy link
Contributor

Sarev0k commented May 7, 2021

There's no equivalent to ItemUtils in v2 because the way attributes are mapped in the DynamoDB Enhanced client has changed.

If you know the types you're working with, you can write an equivalent to ItemUtils using the DefaultAttributeConverterProvider.
E.g. if you want to convert a Map<String, Number>, you can do it with something like:

DefaultAttributeConverterProvider.create()
        .converterFor(EnhancedType.mapOf(String.class, Integer.class))
        .transformFrom(Collections.singletonMap("Key", 5))
// Returns AttributeValue(SS=[], NS=[], BS=[], M={Key=AttributeValue(N=5, SS=[], NS=[], BS=[], M={}, L=[])}, L=[])

If you need "dynamic" type discovery, you'd need to use a wrapper that dynamically selects the converter type.

I'm sorry, this explanation doesn't make sense. This stack overflow answer describes a way of accomplishing this which is exactly what ItemUtils used to do in the V1 SDK.

In fact, you used to have an ItemUtils implementation in the V2 SDK, that you have subsequently removed from your tests.

Can you please explain why an ItemUtils implementation is not technically possible for the V2 SDK, because I believe it is and you're choosing not to implement it for an unknown reason.

@dscalzi
Copy link

dscalzi commented Jun 22, 2021

I don't see why converting a Map<String, AttributeValue> to Map<String, Object> can't be supported out of the box. For my use cases this is a significant degradation from v1 to v2, now that we have to write and maintain this missing core feature in house.

@reply2srij
Copy link

Another vote to bring this back if possible. Much needed feature.

@debora-ito
Copy link
Member

We are considering implementing a v2 ItemUtils equivalent, but we need your help to better understand its use cases. Please follow my comment in #2628 and share what you'd like to see in the v2 ItemUtils.

aws-sdk-java-automation added a commit that referenced this issue Mar 30, 2022
…064d36819

Pull request: release <- staging/b4c6a405-f096-4f46-a8da-144064d36819
@GMCarlos
Copy link

GMCarlos commented Jul 20, 2022

I created a method, I hope it helps u:

public static Map<String,Object> mapToJson(Map<String,AttributeValue> keyValueMap){
        Map<String,Object> finalKeyValueMap = new HashMap();
        for(Map.Entry<String, AttributeValue> entry : keyValueMap.entrySet()){
            if(entry.getValue().type().name() != "M") {
                if(entry.getValue().n() != null){
                    finalKeyValueMap.put('"'+entry.getKey()+'"','"'+entry.getValue().n()+'"');
                }else{
                    if(entry.getValue().s() != null){
                        finalKeyValueMap.put('"' +entry.getKey()+'"','"'+entry.getValue().s()+'"');
                    }else{
                        if(entry.getValue().l() != null){
                            for (int i=0; i<entry.getValue().l().size(); i++)
                                finalKeyValueMap.put('"' +entry.getKey()+'"',mapToJson(entry.getValue().l().get(i).m()));
                        }
                    }
                }
            } else {
                finalKeyValueMap.put('"' +entry.getKey()+'"',mapToJson(entry.getValue().m()));
            }
        }
        return finalKeyValueMap;
    }

And when you get the map, to pass it to json just:

response =  mapToJson(AttributeValue.m());
String delimiterChanged = response.toString();
delimiterChanged = delimiterChanged.replace("=",":");
System.out.println(delimiterChanged);

Enjoy ;)

@brianmaresca
Copy link

brianmaresca commented Dec 10, 2022

it's amazing that this was never resolved. i came up with this for testing purposes. i have many custom dynamo and jackson converters and wanted to look at the raw values that were saved in dynamo to ensure that the converters work as expected.

public static Map<String, Object> toMap(Map<String, AttributeValue> values) {
  Map<String, Object> out = new HashMap<>();
  Optional.ofNullable(values).ifPresent(vals -> vals.forEach((k, v) -> out.put(k, transform(v))));
  return out.size() > 0 ? out : null;
}

public static JSONObject toJSON(Map<String, AttributeValue> values) {
  return new JSONObject(toMap(values));
}

public static Object transform(AttributeValue attributeValue) {
  return Optional.ofNullable(attributeValue)
      .filter(value -> value.type() != null && value.type() != Type.UNKNOWN_TO_SDK_VERSION)
      .map(value -> {
        switch (value.type()) {
          case S:
            return value.s();
          case N:
            return new BigDecimal(value.n());
          case B:
            return BinaryUtils.copyAllBytesFrom(value.b().asByteBuffer());
          case SS:
            return new LinkedHashSet<>(value.ss());
          case NS:
            return value.ns().stream()
                .map(BigDecimal::new)
                .collect(Collectors.toCollection(LinkedHashSet::new));
          case BS:
            return value.bs().stream()
                .map(BytesWrapper::asByteBuffer)
                .map(BinaryUtils::copyAllBytesFrom)
                .collect(Collectors.toSet());
          case M: ;
            Map<String, Object> out = new HashMap<>();
            value.m().forEach((k, v) -> out.put(k, transform(v)));
            return out;
          case L:
            return value.l().stream()
                .map(ItemUtil::transform)
                .collect(Collectors.toList());
          case BOOL:
            return value.bool();
          case NUL:
            return null;
          default:
            return null;
        }}).orElse(null);
}
...
table.putItem(myPojo);
Map<String, AttributeValue> item = dynamoDbClient.getItem(GetItemRequest.builder()
  .tableName("table name")
  .key(Map.of("id", AttributeValue.fromS(id)))
  .build()).item();

JSONObject expectedJson = new JSONObject();
// build the expected json object ..
  
JSONObject itemJson = toJSON(item);
JSONAssert.assertEquals(expectedJson, itemJson, true)

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.
Projects
None yet
Development

No branches or pull requests

7 participants