Skip to content

Commit 50a4e6b

Browse files
committed
Fixed the behavior of REST-XML and REST-JSON services that respond with no payload.
Previously, REST-XML services would treat missing payloads as empty payloads, and REST-JSON services would throw a NullPointerException.
1 parent 734cc1e commit 50a4e6b

File tree

5 files changed

+53
-5
lines changed

5 files changed

+53
-5
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"type": "bugfix",
3+
"category": "AWS SDK for Java v2",
4+
"description": "Fixed an issue where successful JSON service responses were required to include a payload (fixes NullPointerException originating from JsonProtocolUnmarshaller)."
5+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"type": "bugfix",
3+
"category": "AWS SDK for Java v2",
4+
"description": "Fixed an issue with XML services, where the service responding with no payload would treat the payload as empty. Now, empty payloads will properly be populated within the XML response as \"null\"."
5+
}

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,8 @@ public T unmarshall(JsonUnmarshallerContext context,
164164

165165
public <TypeT extends SdkPojo> TypeT unmarshall(SdkPojo sdkPojo,
166166
SdkHttpFullResponse response) throws IOException {
167-
if (hasPayloadMembersOnUnmarshall(sdkPojo) && !hasExplicitBlobPayloadMember(sdkPojo)) {
168-
SdkJsonNode jsonNode = parser.parse(ReleasableInputStream.wrap(response.content().orElse(null)).disableClose());
167+
if (hasPayloadMembersOnUnmarshall(sdkPojo) && !hasExplicitBlobPayloadMember(sdkPojo) && response.content().isPresent()) {
168+
SdkJsonNode jsonNode = parser.parse(ReleasableInputStream.wrap(response.content().get()).disableClose());
169169
return unmarshall(sdkPojo, response, jsonNode);
170170
} else {
171171
return unmarshall(sdkPojo, response, null);
@@ -204,8 +204,9 @@ private static <TypeT extends SdkPojo> TypeT unmarshallStructured(SdkPojo sdkPoj
204204
SdkJsonNode jsonContent,
205205
JsonUnmarshallerContext context) {
206206
for (SdkField<?> field : sdkPojo.sdkFields()) {
207-
if (isExplicitPayloadMember(field) && field.marshallingType() == MarshallingType.SDK_BYTES) {
208-
field.set(sdkPojo, SdkBytes.fromInputStream(context.response().content().orElse(null)));
207+
if (isExplicitPayloadMember(field) && field.marshallingType() == MarshallingType.SDK_BYTES &&
208+
context.response().content().isPresent()) {
209+
field.set(sdkPojo, SdkBytes.fromInputStream(context.response().content().get()));
209210
} else {
210211
SdkJsonNode jsonFieldContent = getSdkJsonNode(jsonContent, field);
211212
JsonUnmarshaller<Object> unmarshaller = context.getUnmarshaller(field.location(), field.marshallingType());

core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/XmlProtocolUnmarshaller.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ public static XmlProtocolUnmarshaller create() {
5555

5656
public <TypeT extends SdkPojo> TypeT unmarshall(SdkPojo sdkPojo,
5757
SdkHttpFullResponse response) {
58-
5958
XmlElement document = XmlResponseParserUtils.parse(sdkPojo, response);
6059
return unmarshall(sdkPojo, document, response);
6160
}
@@ -81,6 +80,11 @@ SdkPojo unmarshall(XmlUnmarshallerContext context, SdkPojo sdkPojo, XmlElement r
8180
XmlUnmarshaller<Object> unmarshaller = REGISTRY.getUnmarshaller(field.location(), field.marshallingType());
8281

8382
if (root != null && field.location() == MarshallLocation.PAYLOAD) {
83+
if (!context.response().content().isPresent()) {
84+
// This is a payload field, but the service sent no content. Do not populate this field (leave it null).
85+
continue;
86+
}
87+
8488
if (isAttribute(field)) {
8589
root.getOptionalAttributeByName(field.unmarshallLocationName())
8690
.ifPresent(e -> field.set(sdkPojo, e));

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,39 @@
8686
// Empty object
8787
}
8888
}
89+
},
90+
{
91+
"description": "Empty payload responses are unmarshalled",
92+
"given": {
93+
"response": {
94+
"status_code": 204
95+
}
96+
},
97+
98+
"when": {
99+
"action": "unmarshall",
100+
"operation": "AllTypes"
101+
},
102+
"then": {
103+
"deserializedAs": {
104+
}
105+
}
106+
},
107+
{
108+
"description": "Empty payload blob responses are unmarshalled",
109+
"given": {
110+
"response": {
111+
"status_code": 204
112+
}
113+
},
114+
"when": {
115+
"action": "unmarshall",
116+
"operation": "OperationWithExplicitPayloadBlob"
117+
},
118+
"then": {
119+
"deserializedAs": {
120+
}
121+
}
89122
}
90123
// TODO header maps, service models supports this but Java SDK does not (not currently exercised)
91124
// TODO header maps with prefix, services models do not support this, only used by S3

0 commit comments

Comments
 (0)