You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I have some POJO classes I am using as members/attributes in a @DynamoDbBean class. When trying to save an instance of my bean class, it complains that there is no converter found for the POJO members, which makes sense. I am unable to annotate the POJO classes with @DynamoDbBean, but if I could just slap the annotation on, I know that it would work smoothly which tells me that Dynamo might already have the library code to attempt to convert simple POJOS (Strings, primitives, Lists, Maps, nested POJOs, enums, etc) to and from AttributeValues.
I wrote the code below for my own usage and so far it works nicely, but I would love to be able to just use something builtin from the SDKs to accomplish the same, if it exists. Please tell me if it does :)
(Or, if there is a way to apply @DynamoDbBean as a mixin or something?)
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
public class DefaultAttributeConverter {
private static Object convertToType(AttributeValue input, Type type) {
try {
if (type instanceof Class<?> clazz) {
switch (clazz.getCanonicalName()) {
case "java.lang.Integer", "int" -> {
return Integer.valueOf(input.n());
}
case "java.lang.Double", "double" -> {
return Double.valueOf(input.n());
}
case "java.lang.Long", "long" -> {
return Long.valueOf(input.n());
}
case "java.lang.Boolean", "boolean" -> {
return input.bool();
}
case "java.lang.String" -> {
return input.s();
}
default -> {
if (clazz.isEnum()) {
Method valueOf = clazz.getMethod("valueOf", String.class);
return valueOf.invoke(clazz, input.s());
} else if (Object.class.isAssignableFrom(clazz)) {
return convertToPojo(input, clazz);
} else {
throw new RuntimeException("Unsupported type: " + clazz.getCanonicalName());
}
}
}
} else if (type instanceof ParameterizedType parameterizedType){
switch (parameterizedType.getRawType().getTypeName()) {
case "java.util.List" -> {
Type innerType = parameterizedType.getActualTypeArguments()[0];
return input.l().stream()
.map(attributeValue -> convertToType(attributeValue, innerType))
.collect(Collectors.toList());
}
case "java.util.Map" -> {
Type valueType = parameterizedType.getActualTypeArguments()[1];
return input.m().entrySet()
.stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
entry -> convertToType(entry.getValue(), valueType)
));
}
default -> throw new RuntimeException("Unsupported parameterized type " + parameterizedType);
}
} else {
throw new RuntimeException("Unsupported type " + type);
}
} catch (final Exception e) {
throw new RuntimeException("Failed to convert AttributeValue to type " + type, e);
}
}
public static <T> T convertToPojo(AttributeValue input, Class<T> clazz) {
try {
T deserialized = clazz.getDeclaredConstructor().newInstance();
Map<String, Field> fieldMap = Arrays.stream(clazz.getDeclaredFields())
.collect(Collectors.toMap(Field::getName, Function.identity()));
for (Map.Entry<String, AttributeValue> member : input.m().entrySet()) {
AttributeValue attributeValue = member.getValue();
if (fieldMap.containsKey(member.getKey())) {
Field field = fieldMap.get(member.getKey());
field.setAccessible(true);
field.set(deserialized, convertToType(attributeValue, field.getGenericType()));
}
}
return deserialized;
} catch (final Exception e) {
throw new RuntimeException("Failed to convert AttributeValue to type " + clazz.getCanonicalName(), e);
}
}
public static AttributeValue convertToAttributeValue(Object input) {
Class<?> inputType = input.getClass();
if (Number.class.isAssignableFrom(inputType) || List.of("int", "double", "long").contains(inputType.getSimpleName())) {
return AttributeValue.fromN(String.valueOf(input));
} else if (String.class.isAssignableFrom(inputType) || inputType.isEnum()) {
return AttributeValue.fromS(String.valueOf(input));
} else if (Boolean.class.isAssignableFrom(inputType) || "boolean".equals(inputType.getSimpleName())) {
return AttributeValue.fromBool((boolean) input);
} else if (Map.class.isAssignableFrom(inputType)) {
return convertMapToAttributeValue((Map<String, ?>) input);
} else if (List.class.isAssignableFrom(inputType)) {
return convertListToAttributeValue((List<?>) input);
}
if (Object.class.isAssignableFrom(inputType)) {
return convertPojoToAttributeValue(input);
} else {
throw new RuntimeException("Unsupported type: " + inputType.getSimpleName());
}
}
private static AttributeValue convertPojoToAttributeValue(Object input) {
Map<String, AttributeValue> pojoMap = new HashMap<>();
Field[] fields = input.getClass().getDeclaredFields();
try {
for (Field field : fields) {
field.setAccessible(true);
Object fieldValue = field.get(input);
if (Objects.isNull(fieldValue)) {
continue;
}
final String fieldName = field.getName();
pojoMap.put(fieldName, convertToAttributeValue(fieldValue));
}
} catch (final IllegalAccessException e) {
throw new RuntimeException(e);
}
return AttributeValue.fromM(pojoMap);
}
private static AttributeValue convertMapToAttributeValue(Map<String, ?> input) {
Map<String, AttributeValue> mapObject = new HashMap<>();
for (Map.Entry<String, ?> entry : input.entrySet()) {
mapObject.put(entry.getKey().toString(), convertToAttributeValue(entry.getValue()));
}
return AttributeValue.fromM(mapObject);
}
private static AttributeValue convertListToAttributeValue(List<?> input) {
return AttributeValue.fromL(input.stream().map(DefaultAttributeConverter::convertToAttributeValue).collect(Collectors.toList()));
}
}
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
-
I have some POJO classes I am using as members/attributes in a @DynamoDbBean class. When trying to save an instance of my bean class, it complains that there is no converter found for the POJO members, which makes sense. I am unable to annotate the POJO classes with @DynamoDbBean, but if I could just slap the annotation on, I know that it would work smoothly which tells me that Dynamo might already have the library code to attempt to convert simple POJOS (Strings, primitives, Lists, Maps, nested POJOs, enums, etc) to and from AttributeValues.
I wrote the code below for my own usage and so far it works nicely, but I would love to be able to just use something builtin from the SDKs to accomplish the same, if it exists. Please tell me if it does :)
(Or, if there is a way to apply @DynamoDbBean as a mixin or something?)
Beta Was this translation helpful? Give feedback.
All reactions