Skip to content

Commit 87f409b

Browse files
authored
Merge pull request #3714 from aws/adaines/fix-RestJsonSerializesSparseNullMapValues-protocol-test-partials
Refactor JSON marshalling for sparse maps and update test cases
2 parents ed61e17 + acd49b4 commit 87f409b

File tree

9 files changed

+1093
-356
lines changed

9 files changed

+1093
-356
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"core": {
3+
"changeLogMessages": [
4+
"Fixed protocol test RestJsonSerializesSparseNullMapValues by adding proper null value handling in sparse map serialization"
5+
],
6+
"type": "patch",
7+
"updateMinimum": false
8+
}
9+
}

generator/ProtocolTestsGenerator/smithy-dotnet-codegen/src/main/java/software/amazon/smithy/dotnet/codegen/customizations/ProtocolTestCustomizations.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,5 @@ private ProtocolTestCustomizations() {
100100
);
101101
public static final List<String> VNextTests = Arrays.asList(
102102
//These are the tests that are failing in v4 after updating to 1.54.0 and artifacts 1.0.3004.0. Each one needs to be investigated.
103-
"RestJsonSerializesSparseNullMapValues"
104103
);
105104
}

generator/ServiceClientGeneratorLib/Generators/Marshallers/JsonRPCStructureMarshaller.cs

Lines changed: 754 additions & 280 deletions
Large diffs are not rendered by default.

generator/ServiceClientGeneratorLib/Generators/Marshallers/JsonRPCStructureMarshaller.tt

Lines changed: 127 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,12 @@ namespace <#= this.Config.Namespace #>.Model.Internal.MarshallTransformat
4949
<#=new string(' ', level * 4)#> {
5050
<#=new string(' ', level * 4)#> context.Writer.WritePropertyName("<#=member.MarshallName#>");
5151
<#+
52-
string memberProperty = variableName + "." + member.PropertyName + (member.IsNullable ? ".Value" : string.Empty);
52+
string memberProperty = variableName + "." + member.PropertyName;
53+
// Only append .Value for nullable types that aren't already complex types
54+
if (member.IsNullable && !member.IsStructure && !member.IsList && !member.IsMap)
55+
{
56+
memberProperty += ".Value";
57+
}
5358
if(member.IsStructure || member.IsList || member.IsMap)
5459
{
5560
this.ProcessStructure(level + 1, variableName + "." + member.PropertyName, member.Shape);
@@ -153,19 +158,7 @@ namespace <#= this.Config.Namespace #>.Model.Internal.MarshallTransformat
153158
}
154159
else if(structure.IsMap)
155160
{
156-
#>
157-
<#=new string(' ', level * 4)#> context.Writer.WriteStartObject();
158-
<#=new string(' ', level * 4)#> foreach (var <#=flatVariableName#>Kvp in <#=variableName#>)
159-
<#=new string(' ', level * 4)#> {
160-
<#=new string(' ', level * 4)#> context.Writer.WritePropertyName(<#=flatVariableName#>Kvp.Key);
161-
<#=new string(' ', level * 4)#> var <#=flatVariableName#>Value = <#=flatVariableName#>Kvp.Value;
162-
163-
<#+
164-
ProcessStructure(level + 1, flatVariableName + "Value", structure.ValueShape);
165-
#>
166-
<#=new string(' ', level * 4)#> }
167-
<#=new string(' ', level * 4)#> context.Writer.WriteEndObject();
168-
<#+
161+
ProcessMap(level, variableName, structure);
169162
}
170163
else if(structure.IsDocument)
171164
{
@@ -207,8 +200,126 @@ namespace <#= this.Config.Namespace #>.Model.Internal.MarshallTransformat
207200

208201
}
209202
}
203+
204+
protected void ProcessMap(int level, string variableName, Shape structure)
205+
{
206+
string flatVariableName = variableName.Replace(".", "");
207+
#>
208+
<#=new string(' ', level * 4)#> context.Writer.WriteStartObject();
209+
<#=new string(' ', level * 4)#> foreach (var <#=flatVariableName#>Kvp in <#=variableName#>)
210+
<#=new string(' ', level * 4)#> {
211+
<#=new string(' ', level * 4)#> context.Writer.WritePropertyName(<#=flatVariableName#>Kvp.Key);
212+
<#=new string(' ', level * 4)#> var <#=flatVariableName#>Value = <#=flatVariableName#>Kvp.Value;
213+
214+
<#+ // Check for null values - only null checks for sparse maps as defined in customizations
215+
216+
bool isNullableMap = false;
217+
if (this.Structure != null)
218+
{
219+
var memberWithMap = this.Structure.Members.FirstOrDefault(m => m.PropertyName == variableName.Split('.').Last());
220+
isNullableMap = (memberWithMap != null && memberWithMap.UseNullable);
221+
}
222+
223+
if (isNullableMap)
224+
{
225+
#>
226+
<#=new string(' ', level * 4)#> if (<#=flatVariableName#>Value == null)
227+
<#=new string(' ', level * 4)#> {
228+
<#=new string(' ', level * 4)#> context.Writer.WriteNullValue();
229+
<#=new string(' ', level * 4)#> }
230+
<#=new string(' ', level * 4)#> else
231+
<#=new string(' ', level * 4)#> {
232+
<#+
233+
}
234+
235+
if(structure.ValueShape.IsBoolean)
236+
{
237+
if(isNullableMap)
238+
{
210239
#>
240+
<#=new string(' ', level * 4)#> context.Writer.WriteBooleanValue(<#=flatVariableName#>Value.Value);
211241
<#+
242+
}
243+
else
244+
{
245+
#>
246+
<#=new string(' ', level * 4)#> context.Writer.WriteBooleanValue(<#=flatVariableName#>Value);
247+
<#+
248+
}
249+
}
250+
else if(structure.ValueShape.IsInt || structure.ValueShape.IsLong || structure.ValueShape.IsFloat || structure.ValueShape.IsDouble)
251+
{
252+
if(isNullableMap)
253+
{
254+
#>
255+
<#=new string(' ', level * 4)#> context.Writer.WriteNumberValue(<#=flatVariableName#>Value.Value);
256+
<#+
257+
}
258+
else
259+
{
260+
#>
261+
<#=new string(' ', level * 4)#> context.Writer.WriteNumberValue(<#=flatVariableName#>Value);
262+
<#+
263+
}
264+
}
265+
else if(structure.ValueShape.IsTimeStamp)
266+
{
267+
if(isNullableMap)
268+
{
269+
if (structure.ValueShape.data[Shape.TimestampFormatKey] != null && !string.Equals(structure.ValueShape.data["timestampFormat"].ToString(), "unixTimestamp"))
270+
{
271+
#>
272+
<#=new string(' ', level * 4)#> context.Writer.WriteStringValue(<#=flatVariableName#>Value.Value);
273+
<#+
274+
}
275+
else
276+
{
277+
#>
278+
<#=new string(' ', level * 4)#> context.Writer.WriteNumberValue(Convert.ToInt64(StringUtils.FromDateTimeToUnixTimestamp(<#=flatVariableName#>Value.Value)));
279+
<#+
280+
}
281+
}
282+
else
283+
{
284+
if (structure.ValueShape.data[Shape.TimestampFormatKey] != null && !string.Equals(structure.ValueShape.data["timestampFormat"].ToString(), "unixTimestamp"))
285+
{
286+
#>
287+
<#=new string(' ', level * 4)#> context.Writer.WriteStringValue(<#=flatVariableName#>Value);
288+
<#+
289+
}
290+
else
291+
{
292+
#>
293+
<#=new string(' ', level * 4)#> context.Writer.WriteNumberValue(Convert.ToInt64(StringUtils.FromDateTimeToUnixTimestamp(<#=flatVariableName#>Value)));
294+
<#+
295+
}
296+
}
297+
}
298+
else
299+
{
300+
if(isNullableMap)
301+
{
302+
ProcessStructure(level + 2, flatVariableName + "Value", structure.ValueShape);
303+
}
304+
else
305+
{
306+
ProcessStructure(level + 1, flatVariableName + "Value", structure.ValueShape);
307+
}
308+
}
309+
310+
// Close the else block for nullable maps
311+
if (isNullableMap)
312+
{
313+
#>
314+
<#=new string(' ', level * 4)#> }
315+
<#+
316+
}
317+
#>
318+
<#=new string(' ', level * 4)#> }
319+
<#=new string(' ', level * 4)#> context.Writer.WriteEndObject();
320+
<#+
321+
}
322+
212323
protected void DetermineCustomMarshallerJsonWriteMethod(Member member, string memberProperty, int level)
213324
{
214325
if (String.Equals(member.CustomMarshallerTransformation,"Amazon.Util.AWSSDKUtils.ConvertToUnixEpochMilliseconds", StringComparison.OrdinalIgnoreCase))
@@ -224,8 +335,7 @@ namespace <#= this.Config.Namespace #>.Model.Internal.MarshallTransformat
224335
<#+
225336
}
226337
}
227-
#>
228-
<#+
338+
229339
/// https://smithy.io/2.0/aws/protocols/aws-restjson1-protocol.html#json-shape-serialization
230340
/// timestamps in json protocols use unixtimestamp if none is specified (by default)
231341
protected void DetermineNormalJsonWriteMethod(Shape shape, string memberProperty, int level)
@@ -256,6 +366,7 @@ namespace <#= this.Config.Namespace #>.Model.Internal.MarshallTransformat
256366
}
257367
else if (shape.IsInt || shape.IsLong || shape.IsFloat || shape.IsDouble)
258368
{
369+
// Simple numeric value
259370
#>
260371
<#=new string(' ', level * 4)#> context.Writer.WriteNumberValue(<#=memberProperty#>);
261372
<#+

sdk/test/ProtocolTests/Generated/RestJsonProtocol/dotnet-protocol-test-codegen/SparseJsonMaps.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,6 @@ public void RestJsonSparseJsonMapsRequest()
8686
/// <summary>
8787
/// Serializes JSON map values in sparse maps
8888
/// </summary>
89-
/*
90-
* This test either requires a breaking change and will be addressed
91-
* in V4, or has a backlog item to be fixed in the future. Please
92-
* refer to the VNextTests list to see which it is.
93-
* */
94-
[Ignore]
9589
[TestMethod]
9690
[TestCategory("ProtocolTest")]
9791
[TestCategory("RequestTest")]

0 commit comments

Comments
 (0)