15
15
16
16
package software .amazon .smithy .aws .typescript .codegen ;
17
17
18
+ import java .util .ArrayList ;
19
+ import java .util .List ;
18
20
import java .util .Map ;
21
+ import java .util .function .BiConsumer ;
19
22
import java .util .function .Supplier ;
23
+ import java .util .stream .Collectors ;
20
24
import software .amazon .smithy .codegen .core .CodegenException ;
21
25
import software .amazon .smithy .model .Model ;
22
26
import software .amazon .smithy .model .shapes .CollectionShape ;
@@ -99,17 +103,23 @@ protected void deserializeStructure(GenerationContext context, StructureShape sh
99
103
members .forEach ((memberName , memberShape ) -> writer .write ("$L: undefined," , memberName ));
100
104
});
101
105
102
- members .forEach ((memberName , memberShape ) ->
103
- deserializeNamedStructureMember (context , memberName , memberShape , () -> "output" ));
106
+ members .forEach ((memberName , memberShape ) -> {
107
+ // Grab the target shape so we can use a member deserializer on it.
108
+ Shape target = context .getModel ().expectShape (memberShape .getTarget ());
109
+ deserializeNamedMember (context , memberName , memberShape , () -> "output" , (dataSource , visitor ) -> {
110
+ writer .write ("contents.$L = $L;" , memberName , target .accept (visitor ));
111
+ });
112
+ });
104
113
105
114
writer .write ("return contents;" );
106
115
}
107
116
108
- void deserializeNamedStructureMember (
117
+ void deserializeNamedMember (
109
118
GenerationContext context ,
110
119
String memberName ,
111
120
MemberShape memberShape ,
112
- Supplier <String > inputLocation
121
+ Supplier <String > inputLocation ,
122
+ BiConsumer <String , DocumentMemberDeserVisitor > statementBody
113
123
) {
114
124
TypeScriptWriter writer = context .getWriter ();
115
125
Model model = context .getModel ();
@@ -122,28 +132,42 @@ void deserializeNamedStructureMember(
122
132
Shape target = context .getModel ().expectShape (memberShape .getTarget ());
123
133
// Handle @xmlFlattened for collections and maps.
124
134
boolean isFlattened = memberShape .hasTrait (XmlFlattenedTrait .class );
135
+ // Handle targets that return multiple entities per member.
136
+ boolean deserializationReturnsArray = deserializationReturnsArray (target );
125
137
126
138
// Build a string based on the traits that represents the location.
127
139
// Note we don't need to handle @xmlAttribute here because the parser flattens
128
140
// attributes in to the main structure.
129
141
StringBuilder sourceBuilder = new StringBuilder (inputLocation .get ())
130
142
.append ("['" ).append (locationName ).append ("']" );
131
-
132
- // Go in to a specialized element for unflattened aggregates
133
- if (deserializationReturnsArray (target ) && !isFlattened ) {
143
+ // Track any locations we need to validate on our way to the final element.
144
+ List <String > locationsToValidate = new ArrayList <>();
145
+
146
+ // Go in to a specialized element for unflattened aggregates.
147
+ if (deserializationReturnsArray && !isFlattened ) {
148
+ // Make sure we validate the wrapping element is set.
149
+ locationsToValidate .add (sourceBuilder .toString ());
150
+ // Update the target element.
134
151
String targetLocation = getUnnamedAggregateTargetLocation (model , target );
135
152
sourceBuilder .append ("['" ).append (targetLocation ).append ("']" );
136
153
}
137
154
138
155
// Handle the response property.
139
156
String source = sourceBuilder .toString ();
140
- writer .openBlock ("if ($L !== undefined) {" , "}" , source , () -> {
141
- if (isFlattened ) {
157
+ // Validate the resulting target element is set.
158
+ locationsToValidate .add (source );
159
+ // Build a string of the elements to validate before deserializing.
160
+ String validationStatement = locationsToValidate .stream ()
161
+ .map (location -> location + " !== undefined" )
162
+ .collect (Collectors .joining (" && " ));
163
+ writer .openBlock ("if ($L) {" , "}" , validationStatement , () -> {
164
+ String dataSource = source ;
165
+ // The XML parser will set one K:V for a member that could return multiple entries but only has one.
166
+ if (deserializationReturnsArray ) {
142
167
writer .write ("const wrappedItem = ($1L instanceof Array) ? $1L : [$1L];" , source );
168
+ dataSource = "wrappedItem" ;
143
169
}
144
- writer .write ("contents.$L = $L;" , memberName ,
145
- // Dispatch to the output value provider for any additional handling.
146
- target .accept (getMemberVisitor (isFlattened ? "wrappedItem" : source )));
170
+ statementBody .accept (dataSource , getMemberVisitor (dataSource ));
147
171
});
148
172
}
149
173
@@ -165,38 +189,16 @@ private String getUnnamedAggregateTargetLocation(Model model, Shape shape) {
165
189
@ Override
166
190
protected void deserializeUnion (GenerationContext context , UnionShape shape ) {
167
191
TypeScriptWriter writer = context .getWriter ();
168
- Model model = context .getModel ();
169
192
170
193
// Check for any known union members and return when we find one.
171
194
Map <String , MemberShape > members = shape .getAllMembers ();
172
195
members .forEach ((memberName , memberShape ) -> {
173
- // Use the @xmlName trait if present on the member, otherwise use the member name.
174
- String locationName = memberShape .getTrait (XmlNameTrait .class )
175
- .map (XmlNameTrait ::getValue )
176
- .orElse (memberName );
177
196
// Grab the target shape so we can use a member deserializer on it.
178
- Shape target = context .getModel ()
179
- .expectShape (memberShape .getTarget ());
180
- // Handle @xmlFlattened for collections and maps.
181
- boolean isFlattened = memberShape .hasTrait (XmlFlattenedTrait .class );
182
-
183
- // Build a string based on the traits that represents the location.
184
- // Note we don't need to handle @xmlAttribute here because the parser flattens
185
- // attributes in to the main structure.
186
- StringBuilder sourceBuilder = new StringBuilder ("output['" ).append (locationName ).append ("']" );
187
-
188
- // Go in to a specialized element for unflattened aggregates
189
- if (deserializationReturnsArray (target ) && !isFlattened ) {
190
- String targetLocation = getUnnamedAggregateTargetLocation (model , target );
191
- sourceBuilder .append ("['" ).append (targetLocation ).append ("']" );
192
- }
193
-
194
- // Handle the response property.
195
- String source = sourceBuilder .toString ();
196
- writer .openBlock ("if ($L !== undefined) {" , "}" , source , () -> {
197
+ Shape target = context .getModel ().expectShape (memberShape .getTarget ());
198
+ deserializeNamedMember (context , memberName , memberShape , () -> "output" , (dataSource , visitor ) -> {
197
199
writer .openBlock ("return {" , "};" , () -> {
198
200
// Dispatch to the output value provider for any additional handling.
199
- writer .write ("$L: $L" , memberName , target .accept (getMemberVisitor ( source ) ));
201
+ writer .write ("$L: $L" , memberName , target .accept (visitor ));
200
202
});
201
203
});
202
204
});
0 commit comments