Skip to content

Commit 3ec934d

Browse files
authored
Set explicit payload members to null if the input is empty (#6111)
* Set explicit payload members to null if the input is empty * Make checkstyle happy
1 parent 890899f commit 3ec934d

File tree

4 files changed

+59
-1
lines changed

4 files changed

+59
-1
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"category": "AWS SDK for Java v2",
3+
"contributor": "",
4+
"type": "bugfix",
5+
"description": "Fix a regression for the JSON REST protocol for which an structure explicit payload member was set to the empty object instead of null"
6+
}

core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonProtocolUnmarshaller.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.util.List;
2828
import java.util.Map;
2929
import java.util.Optional;
30+
import java.util.function.Supplier;
3031
import software.amazon.awssdk.annotations.SdkInternalApi;
3132
import software.amazon.awssdk.annotations.ThreadSafe;
3233
import software.amazon.awssdk.core.SdkBytes;
@@ -266,6 +267,11 @@ private <T extends SdkPojo> T unmarshallFromJson(SdkPojo sdkPojo, InputStream in
266267
return (T) unmarshallingParser.parse(sdkPojo, inputStream);
267268
}
268269

270+
@SuppressWarnings("unchecked")
271+
private <T extends SdkPojo> T unmarshallMemberFromJson(Supplier<SdkPojo> constructor, InputStream inputStream) {
272+
return (T) unmarshallingParser.parseMember(constructor, inputStream);
273+
}
274+
269275
private <TypeT extends SdkPojo> TypeT unmarshallResponse(SdkPojo sdkPojo,
270276
SdkHttpFullResponse response) throws IOException {
271277
JsonUnmarshallerContext context = JsonUnmarshallerContext.builder()
@@ -290,7 +296,7 @@ private <TypeT extends SdkPojo> TypeT unmarshallResponse(SdkPojo sdkPojo,
290296
} else if (isExplicitPayloadMember(field) && field.marshallingType() == MarshallingType.SDK_POJO) {
291297
Optional<AbortableInputStream> responseContent = context.response().content();
292298
if (responseContent.isPresent()) {
293-
field.set(sdkPojo, unmarshallFromJson(field.constructor().get(), responseContent.get()));
299+
field.set(sdkPojo, unmarshallMemberFromJson(field.constructor(), responseContent.get()));
294300
} else {
295301
field.set(sdkPojo, null);
296302
}

core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonUnmarshallingParser.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.util.LinkedHashMap;
2525
import java.util.List;
2626
import java.util.Map;
27+
import java.util.function.Supplier;
2728
import software.amazon.awssdk.annotations.SdkInternalApi;
2829
import software.amazon.awssdk.annotations.ThreadSafe;
2930
import software.amazon.awssdk.core.SdkBytes;
@@ -72,6 +73,33 @@ public static Builder builder() {
7273
return new Builder();
7374
}
7475

76+
/**
77+
* Parse the provided {@link InputStream} and return the deserialized {@link SdkPojo}. Unlike
78+
* {@link #parse(SdkPojo, InputStream)} this method returns null if the input stream is empty. This is used to unmarshall
79+
* payload members that can be null unlike top-level response pojos.
80+
*/
81+
public SdkPojo parseMember(Supplier<SdkPojo> constructor, InputStream content) {
82+
return invokeSafely(() -> {
83+
try (JsonParser parser = jsonFactory.createParser(content)
84+
.configure(JsonParser.Feature.AUTO_CLOSE_SOURCE, false)) {
85+
86+
JsonUnmarshallerContext c = JsonUnmarshallerContext.builder().build();
87+
JsonToken token = parser.nextToken();
88+
if (token == null) {
89+
return null;
90+
}
91+
if (token == JsonToken.VALUE_NULL) {
92+
return null;
93+
}
94+
if (token != JsonToken.START_OBJECT) {
95+
throw new JsonParseException("expecting start object, got instead: " + token);
96+
}
97+
SdkPojo pojo = constructor.get();
98+
return parseSdkPojo(c, pojo, parser);
99+
}
100+
});
101+
}
102+
75103
/**
76104
* Parse the provided {@link InputStream} and return the deserialized {@link SdkPojo}.
77105
*/

test/protocol-tests-core/src/main/resources/software/amazon/awssdk/protocol/suites/cases/rest-json-output.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,24 @@
1919
}
2020
}
2121
},
22+
{
23+
"description": "Operation with explicit payload structure, with emtpy output is unmarshalled as null value",
24+
"given": {
25+
"response": {
26+
"status_code": 200,
27+
"body": ""
28+
}
29+
},
30+
"when": {
31+
"action": "unmarshall",
32+
"operation": "OperationWithExplicitPayloadStructure"
33+
},
34+
"then": {
35+
"deserializedAs": {
36+
"PayloadMember": null
37+
}
38+
}
39+
},
2240
{
2341
"description": "Operation with streaming payload in output is unmarshalled correctly",
2442
"given": {

0 commit comments

Comments
 (0)