Skip to content

Commit 7b32252

Browse files
authored
Merge pull request #131 from skotambkar/xml/Deser
2 parents b29a2bc + eadd63b commit 7b32252

File tree

9 files changed

+508
-16
lines changed

9 files changed

+508
-16
lines changed

codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public final class SmithyGoDependency {
3939
public static final GoDependency CRYPTORAND = stdlib("crypto/rand", "cryptorand");
4040
public static final GoDependency TESTING = stdlib("testing");
4141
public static final GoDependency ERRORS = stdlib("errors");
42+
public static final GoDependency XML = stdlib("encoding/xml");
4243

4344
public static final GoDependency SMITHY = smithy(null, "smithy");
4445
public static final GoDependency SMITHY_HTTP_TRANSPORT = smithy("transport/http", "smithyhttp");

codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/DocumentShapeDeserVisitor.java

+5-5
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@
4141
/**
4242
* Visitor to generate deserialization functions for shapes in protocol document bodies.
4343
*
44-
* Visitor methods for aggregate types are final and will generate functions that dispatch
45-
* their loading from the body to the matching abstract method.
44+
* Visitor methods for aggregate types except maps and collections are final and will
45+
* generate functions that dispatch their loading from the body to the matching abstract method.
4646
*
4747
* Visitor methods for all other types will default to not generating deserialization
4848
* functions. This may be overwritten by downstream implementations if the protocol requires
@@ -472,7 +472,7 @@ public final Void documentShape(DocumentShape shape) {
472472
* @return null
473473
*/
474474
@Override
475-
public final Void listShape(ListShape shape) {
475+
public Void listShape(ListShape shape) {
476476
generateDeserFunction(shape, (c, s) -> deserializeCollection(c, s.asListShape().get()));
477477
return null;
478478
}
@@ -484,7 +484,7 @@ public final Void listShape(ListShape shape) {
484484
* @return null
485485
*/
486486
@Override
487-
public final Void mapShape(MapShape shape) {
487+
public Void mapShape(MapShape shape) {
488488
generateDeserFunction(shape, (c, s) -> deserializeMap(c, s.asMapShape().get()));
489489
return null;
490490
}
@@ -496,7 +496,7 @@ public final Void mapShape(MapShape shape) {
496496
* @return null
497497
*/
498498
@Override
499-
public final Void setShape(SetShape shape) {
499+
public Void setShape(SetShape shape) {
500500
generateDeserFunction(shape, (c, s) -> deserializeCollection(c, s.asSetShape().get()));
501501
return null;
502502
}

codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/HttpBindingProtocolGenerator.java

+1
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ private void generateOperationDeserializerMiddleware(GenerationContext context,
280280
ApplicationProtocol applicationProtocol = getApplicationProtocol();
281281
Symbol responseType = applicationProtocol.getResponseType();
282282
GoWriter goWriter = context.getWriter();
283+
283284
String errorFunctionName = ProtocolGenerator.getOperationErrorDeserFunctionName(
284285
operation, context.getProtocolName());
285286

codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/HttpProtocolGeneratorUtils.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ private HttpProtocolGeneratorUtils() {}
4545
* and {@code errorMessage} variables from the http response.
4646
* @return A set of all error structure shapes for the operation that were dispatched to.
4747
*/
48-
static Set<StructureShape> generateErrorDispatcher(
48+
static Set<StructureShape> generateErrorDispatcher(
4949
GenerationContext context,
5050
OperationShape operation,
5151
Symbol responseType,
@@ -78,13 +78,14 @@ static Set<StructureShape> generateErrorDispatcher(
7878
// Dispatch to the message/code generator to try to get the specific code and message.
7979
errorMessageCodeGenerator.accept(context);
8080

81-
writer.openBlock("switch errorCode {", "}", () -> {
81+
writer.openBlock("switch {", "}", () -> {
8282
new TreeSet<>(operation.getErrors()).forEach(errorId -> {
8383
StructureShape error = context.getModel().expectShape(errorId).asStructureShape().get();
8484
errorShapes.add(error);
8585
String errorDeserFunctionName = ProtocolGenerator.getErrorDeserFunctionName(
8686
error, context.getProtocolName());
87-
writer.openBlock("case $S:", "", errorId.getName(), () -> {
87+
writer.addUseImports(SmithyGoDependency.STRINGS);
88+
writer.openBlock("case strings.EqualFold($S, errorCode):", "", errorId.getName(), () -> {
8889
writer.write("return $L(response, errorBody)", errorDeserFunctionName);
8990
});
9091
});

codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/HttpRpcProtocolGenerator.java

+8-8
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ private void generateOperationSerializer(GenerationContext context, OperationSha
120120
writer.write("request.Request.URL.Path = $S", getOperationPath(context, operation));
121121
writer.write("request.Request.Method = \"POST\"");
122122
writer.write("httpBindingEncoder, err := httpbinding.NewEncoder(request.URL.Path, "
123-
+ "request.URL.RawQuery, request.Header)");
123+
+ "request.URL.RawQuery, request.Header)");
124124
writer.openBlock("if err != nil {", "}", () -> {
125125
writer.write("return out, metadata, &smithy.SerializationError{Err: err}");
126126
});
@@ -160,16 +160,16 @@ private void writeRequestHeaders(GenerationContext context, OperationShape opera
160160
* <li>{@code ctx: context.Context}: a type containing context and tools for type serde.</li>
161161
* </ul>
162162
*
163-
* @param context The generation context.
163+
* @param context The generation context.
164164
* @param operation The operation being generated.
165-
* @param writer The writer to use.
165+
* @param writer The writer to use.
166166
*/
167167
protected void writeDefaultHeaders(GenerationContext context, OperationShape operation, GoWriter writer) {}
168168

169169
/**
170170
* Provides the request path for the operation.
171171
*
172-
* @param context The generation context.
172+
* @param context The generation context.
173173
* @param operation The operation being generated.
174174
* @return The path to send HTTP requests to.
175175
*/
@@ -185,7 +185,7 @@ protected void writeDefaultHeaders(GenerationContext context, OperationShape ope
185185
* <li>{@code ctx: context.Context}: a type containing context and tools for type serde.</li>
186186
* </ul>
187187
*
188-
* @param context The generation context.
188+
* @param context The generation context.
189189
* @param operation The operation to serialize for.
190190
*/
191191
protected abstract void serializeInputDocument(GenerationContext context, OperationShape operation);
@@ -204,7 +204,7 @@ public void generateSharedDeserializerComponents(GenerationContext context) {
204204
* {@code deserializeOutputDocument}.
205205
*
206206
* @param context The generation context.
207-
* @param shapes The shapes to generate deserialization for.
207+
* @param shapes The shapes to generate deserialization for.
208208
*/
209209
protected abstract void generateDocumentBodyShapeDeserializers(GenerationContext context, Set<Shape> shapes);
210210

@@ -280,7 +280,7 @@ private void generateOperationDeserializer(GenerationContext context, OperationS
280280
* <li>{@code ctx: context.Context}: a type containing context and tools for type serde.</li>
281281
* </ul>
282282
*
283-
* @param context The generation context
283+
* @param context The generation context
284284
* @param operation The operation to deserialize for.
285285
*/
286286
protected abstract void deserializeOutputDocument(GenerationContext context, OperationShape operation);
@@ -306,7 +306,7 @@ private void generateErrorDeserializer(GenerationContext context, StructureShape
306306
* </ul>
307307
*
308308
* @param context The generation context.
309-
* @param shape The error shape.
309+
* @param shape The error shape.
310310
*/
311311
protected abstract void deserializeError(GenerationContext context, StructureShape shape);
312312

xml/xml_decoder.go

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package xml
2+
3+
import (
4+
"encoding/xml"
5+
"fmt"
6+
)
7+
8+
// NodeDecoder is a XML decoder wrapper that is responsible to decoding
9+
// a single XML Node element and it's nested member elements. This wrapper decoder
10+
// takes in the start element of the top level node being decoded.
11+
type NodeDecoder struct {
12+
Decoder *xml.Decoder
13+
StartEl xml.StartElement
14+
}
15+
16+
// WrapNodeDecoder returns an initialized XMLNodeDecoder
17+
func WrapNodeDecoder(decoder *xml.Decoder, startEl xml.StartElement) NodeDecoder {
18+
return NodeDecoder{
19+
Decoder: decoder,
20+
StartEl: startEl,
21+
}
22+
}
23+
24+
// Token on a Node Decoder returns a xml StartElement. It returns a boolean that indicates the
25+
// a token is the node decoder's end node token; and an error which indicates any error
26+
// that occurred while retrieving the start element
27+
func (d NodeDecoder) Token() (t xml.StartElement, done bool, err error) {
28+
for {
29+
token, e := d.Decoder.Token()
30+
if e != nil {
31+
return t, done, e
32+
}
33+
34+
// check if we reach end of the node being decoded
35+
if el, ok := token.(xml.EndElement); ok {
36+
return t, el == d.StartEl.End(), err
37+
}
38+
39+
if t, ok := token.(xml.StartElement); ok {
40+
return t, false, err
41+
}
42+
43+
// skip token if it is a comment or preamble or empty space value due to indentation
44+
// or if it's a value and is not expected
45+
}
46+
47+
return
48+
}
49+
50+
// Value provides an abstraction to retrieve char data value within an xml element.
51+
// The method will return an error if it encounters a nested xml element instead of char data.
52+
// This method should only be used to retrieve simple type or blob shape values as []byte.
53+
func (d NodeDecoder) Value() (c []byte, done bool, err error) {
54+
t, e := d.Decoder.Token()
55+
if e != nil {
56+
return c, done, e
57+
}
58+
59+
// check if token is of type charData
60+
if ev, ok := t.(xml.CharData); ok {
61+
return ev, done, err
62+
}
63+
64+
if ev, ok := t.(xml.EndElement); ok {
65+
if ev == d.StartEl.End() {
66+
return c, true, err
67+
}
68+
}
69+
70+
return c, done, fmt.Errorf("expected value for %v element, got %T type %v instead", d.StartEl.Name.Local, t, t)
71+
}
72+
73+
// FetchRootElement takes in a decoder and returns the first start element within the xml body.
74+
// This function is useful in fetching the start element of an XML response and ignore the
75+
// comments and preamble
76+
func FetchRootElement(decoder *xml.Decoder) (startElement xml.StartElement, err error) {
77+
for {
78+
t, e := decoder.Token()
79+
if e != nil {
80+
return startElement, e
81+
}
82+
83+
if startElement, ok := t.(xml.StartElement); ok {
84+
return startElement, err
85+
}
86+
}
87+
}

0 commit comments

Comments
 (0)