The core of problem is that the both AttributeValue and one of its non standart members, SdkBytes are not standard pojos and so don't have suitable Serializer/Deserializer.
Let's make this happen.
Serializer:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import software.amazon.awssdk.core.SdkBytes;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Map;
public class AttributeValueCustomSerializer extends StdSerializer<AttributeValue> {
protected AttributeValueCustomSerializer(Class<AttributeValue> attributeValueClass) {
super(attributeValueClass);
}
public AttributeValueCustomSerializer(){
this(null);
}
@Override
public void serialize(AttributeValue av, JsonGenerator jGen, SerializerProvider serializerProvider) throws IOException {
jGen.writeStartObject();
if(av.s() != null) {
jGen.writeStringField("S", av.s());
}
if(av.n() != null) {
jGen.writeStringField("N", av.n());
}
if(av.b() != null) {
jGen.writeStringField("B", av.b().asString(StandardCharsets.UTF_8));
}
if(av.hasSs()) {
jGen.writeArrayFieldStart("SS");
for (String arg: av.ss()) {
jGen.writeString(arg);
}
jGen.writeEndArray();
}
if(av.hasNs()) {
jGen.writeArrayFieldStart("NS");
for (String arg: av.ns()) {
jGen.writeString(arg);
}
jGen.writeEndArray();
}
if(av.hasBs()) {
jGen.writeArrayFieldStart("BS");
for (SdkBytes arg: av.bs()) {
jGen.writeString(arg.asString(StandardCharsets.UTF_8));
}
jGen.writeEndArray();
}
if(av.hasM()) {
jGen.writeObjectFieldStart("M");
for (Map.Entry<String,AttributeValue> e: av.m().entrySet()) {
jGen.writeObjectField(e.getKey(), e.getValue());
}
jGen.writeEndObject();
}
if(av.hasL()) {
jGen.writeArrayFieldStart("L");
for (AttributeValue arg: av.l()) {
jGen.writeObject(arg);
}
jGen.writeEndArray();
}
if(av.bool() != null) {
jGen.writeBooleanField("BOOL", av.bool());
}
if(av.nul() != null) {
jGen.writeBooleanField("NUL", av.nul());
}
jGen.writeEndObject();
}
}
Deserializer:
import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import software.amazon.awssdk.core.SdkBytes;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import java.io.IOException;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
public class AttributeValueCustomDeserializer extends StdDeserializer<AttributeValue> {
public AttributeValueCustomDeserializer() {
this(null);
}
public AttributeValueCustomDeserializer(Class<?> vc) {
super(vc);
}
private AttributeValue deserialize(JsonNode node) {
AttributeValue.Builder builder = AttributeValue.builder();
if(node.has("S")) {
builder = builder.s(node.get("S").asText());
}
if(node.has("N")) {
builder = builder.n(node.get("N").asText());
}
if(node.has("B")) {
builder = builder.b(SdkBytes.fromUtf8String(node.get("B").asText()));
}
if(node.has("SS")) {
builder = builder.ss(StreamSupport.stream(node.get("SS").spliterator(),false).map(JsonNode::asText)
.collect(Collectors.toList()));
}
if(node.has("NS")) {
builder = builder.ns(StreamSupport.stream(node.get("NS").spliterator(),false).map(JsonNode::asText)
.collect(Collectors.toList()));
}
if(node.has("BS")) {
builder = builder.bs(StreamSupport.stream(node.get("BS").spliterator(),false)
.map(JsonNode::asText).map(SdkBytes::fromUtf8String).collect(Collectors.toList()));
}
if(node.has("M")) {
builder = builder.m(node.get("M").properties().stream().collect(Collectors.toMap(Map.Entry::getKey,
e -> deserialize(e.getValue()))));
}
if(node.has("L")) {
builder = builder.l(StreamSupport.stream(node.get("L").spliterator(),false).map(this::deserialize).collect(Collectors.toList()));
}
if(node.has("BOOL")) {
builder = builder.bool(node.get("BOOL").asBoolean());
}
if(node.has("NUL")) {
builder = builder.nul(node.get("NUL").asBoolean());
}
return builder.build();
}
@Override
public AttributeValue deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JacksonException {
JsonNode node = jp.getCodec().readTree(jp);
return deserialize(node);
}
}
Test/Usage:
void serializeDeserializeTest() throws JsonProcessingException {
// Mapper initialization
ObjectMapper mapper = new ObjectMapper();
//Custom Serializer/Deserializer registration
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(AttributeValue.class, new AttributeValueCustomSerializer());
simpleModule.addDeserializer(AttributeValue.class, new AttributeValueCustomDeserializer());
mapper.registerModule(simpleModule);
AttributeValue originalAttributeValue = generateAttributeValue();
String s = mapper.writeValueAsString(originalAttributeValue);
assertNotNull(s);
assertEquals(originalAttributeValue, mapper.readValue(s,AttributeValue.class));
}
You should only generate the original AttributeValue. I'm using podam, for this porpoise, but it is out of the scope of this post.
Used those two baedung articles about custom serialization and deserialization.