Skip to content

Commit 9e80ac3

Browse files
Add blob support to NodeMapper
This adds blob support to NodeMapper, serializing to and from base64 encoded string nodes.
1 parent 121ac6a commit 9e80ac3

File tree

4 files changed

+72
-0
lines changed

4 files changed

+72
-0
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "feature",
3+
"description": "Added support for (de)serializing byte arrays to/from base64-encoded string nodes to `NodeMapper`.",
4+
"pull_requests": [
5+
"[#2803](https://github.com/smithy-lang/smithy/pull/2803)"
6+
]
7+
}

smithy-model/src/main/java/software/amazon/smithy/model/node/DefaultNodeDeserializers.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@
2020
import java.math.BigInteger;
2121
import java.net.URI;
2222
import java.net.URL;
23+
import java.nio.charset.StandardCharsets;
2324
import java.nio.file.Path;
2425
import java.nio.file.Paths;
2526
import java.util.ArrayList;
27+
import java.util.Base64;
2628
import java.util.Collection;
2729
import java.util.HashMap;
2830
import java.util.HashSet;
@@ -603,6 +605,21 @@ private interface FromStringClassFactory {
603605
};
604606
};
605607

608+
// Creates a byte array from a base64-encoded string node.
609+
private static final ObjectCreatorFactory BYTES_CREATOR = (nodeType, target, nodeMapper) -> {
610+
if (nodeType != NodeType.STRING || target != byte[].class) {
611+
return null;
612+
}
613+
return (node, targetType, pointer, mapper) -> {
614+
String value = node.expectStringNode().getValue();
615+
try {
616+
return Base64.getDecoder().decode(value.getBytes(StandardCharsets.UTF_8));
617+
} catch (Exception e) {
618+
throw NodeDeserializationException.fromContext(targetType, pointer, node, e, e.getMessage());
619+
}
620+
};
621+
};
622+
606623
// The priority ordered list of default factories that NodeMapper uses.
607624
// The priority is determined based on the specificity of each deserializer;
608625
// the most specific ones should appear at the start of the list, and the
@@ -615,6 +632,7 @@ private interface FromStringClassFactory {
615632
NULL_CREATOR,
616633
FROM_NODE_CREATOR,
617634
BOOLEAN_CREATOR_FACTORY,
635+
BYTES_CREATOR,
618636
FROM_STRING,
619637
STRING_CREATOR,
620638
ENUM_CREATOR,

smithy-model/src/main/java/software/amazon/smithy/model/node/DefaultNodeSerializers.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import java.net.URL;
1515
import java.nio.file.Path;
1616
import java.util.ArrayList;
17+
import java.util.Base64;
1718
import java.util.Comparator;
1819
import java.util.HashSet;
1920
import java.util.LinkedHashMap;
@@ -84,6 +85,19 @@ public Node serialize(Number value, Set<Object> serializedObjects, NodeMapper ma
8485
}
8586
};
8687

88+
// Serializes a byte array into a base64-encoded string node.
89+
private static final Serializer<byte[]> BLOB_SERIALIZER = new Serializer<byte[]>() {
90+
@Override
91+
public Class<byte[]> getType() {
92+
return byte[].class;
93+
}
94+
95+
@Override
96+
public Node serialize(byte[] value, Set<Object> serializedObjects, NodeMapper mapper) {
97+
return Node.from(Base64.getEncoder().encodeToString(value));
98+
}
99+
};
100+
87101
// Serialize a String into a StringNode.
88102
private static final Serializer<String> STRING_SERIALIZER = new Serializer<String>() {
89103
@Override
@@ -392,6 +406,7 @@ private boolean canSerialize(NodeMapper mapper, Node value) {
392406
static final List<Serializer> SERIALIZERS = ListUtils.of(
393407
TO_NODE_SERIALIZER,
394408
OPTIONAL_SERIALIZER,
409+
BLOB_SERIALIZER,
395410
STRING_SERIALIZER,
396411
BOOLEAN_SERIALIZER,
397412
NUMBER_SERIALIZER,

smithy-model/src/test/java/software/amazon/smithy/model/node/NodeMapperTest.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@
1616
import static org.hamcrest.Matchers.not;
1717
import static org.hamcrest.Matchers.nullValue;
1818
import static org.hamcrest.Matchers.startsWith;
19+
import static org.junit.jupiter.api.Assertions.assertEquals;
1920

2021
import java.io.File;
2122
import java.net.URI;
2223
import java.net.URL;
24+
import java.nio.charset.StandardCharsets;
2325
import java.nio.file.Path;
2426
import java.util.ArrayList;
2527
import java.util.Arrays;
@@ -1634,4 +1636,34 @@ public Map<ShapeId, List<Map<ShapeId, ShapeType>>> getShapeTypes() {
16341636
return shapeTypes;
16351637
}
16361638
}
1639+
1640+
@Test
1641+
public void serializesBytesToBase64StringNode() {
1642+
NodeMapper mapper = new NodeMapper();
1643+
Node input = Node.objectNode().withMember("bytes", "Zm9v");
1644+
HasBytes result = mapper.deserialize(input, HasBytes.class);
1645+
String decoded = new String(result.getBytes(), StandardCharsets.UTF_8);
1646+
assertEquals("foo", decoded);
1647+
}
1648+
1649+
@Test
1650+
public void deserializesBytesFromBase64StringNode() {
1651+
NodeMapper mapper = new NodeMapper();
1652+
HasBytes input = new HasBytes();
1653+
input.setBytes("foo".getBytes(StandardCharsets.UTF_8));
1654+
ObjectNode result = mapper.serialize(input).expectObjectNode();
1655+
assertEquals("Zm9v", result.expectStringMember("bytes").getValue());
1656+
}
1657+
1658+
public static final class HasBytes {
1659+
private byte[] bytes;
1660+
1661+
public byte[] getBytes() {
1662+
return bytes;
1663+
}
1664+
1665+
public void setBytes(byte[] bytes) {
1666+
this.bytes = bytes;
1667+
}
1668+
}
16371669
}

0 commit comments

Comments
 (0)