Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor XML deserialization #1042

Merged
merged 25 commits into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .changes/fb00b4ae-ffdb-4137-baa8-574848296da1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"id": "fb00b4ae-ffdb-4137-baa8-574848296da1",
"type": "bugfix",
"description": "Refactor XML deserialization to handle flat collections",
"issues": [
"awslabs/aws-sdk-kotlin#1220"
]
}
3 changes: 2 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,8 @@ apiValidation {
"channel-benchmarks",
"http-benchmarks",
"serde-benchmarks",
"serde-benchmarks-codegen",
"serde-codegen-support",
"serde-tests",
"nullability-tests",
"paginator-tests",
"waiter-tests",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,3 +187,7 @@ fun <T : AbstractCodeWriter<T>> T.callIf(test: Boolean, runnable: Runnable): T {
}
return this
}

/** Escape the [expressionStart] character to avoid problems during formatting */
fun <T : AbstractCodeWriter<T>> T.escape(text: String): String =
text.replace("$expressionStart", "$expressionStart$expressionStart")
aajtodd marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ object RuntimeTypes {
val Attributes = symbol("Attributes")
val attributesOf = symbol("attributesOf")
val AttributeKey = symbol("AttributeKey")
val createOrAppend = symbol("createOrAppend")
val get = symbol("get")
val mutableMultiMapOf = symbol("mutableMultiMapOf")
val putIfAbsent = symbol("putIfAbsent")
Expand Down Expand Up @@ -230,6 +231,7 @@ object RuntimeTypes {
val SerialKind = symbol("SerialKind")
val SerializationException = symbol("SerializationException")
val DeserializationException = symbol("DeserializationException")
val getOrDeserializeErr = symbol("getOrDeserializeErr")

val serializeStruct = symbol("serializeStruct")
val serializeList = symbol("serializeList")
Expand All @@ -241,6 +243,18 @@ object RuntimeTypes {
val asSdkSerializable = symbol("asSdkSerializable")
val field = symbol("field")

val parse = symbol("parse")
val parseInt = symbol("parseInt")
val parseShort = symbol("parseShort")
val parseLong = symbol("parseLong")
val parseFloat = symbol("parseFloat")
val parseDouble = symbol("parseDouble")
val parseByte = symbol("parseByte")
val parseBoolean = symbol("parseBoolean")
val parseTimestamp = symbol("parseTimestamp")
val parseBigInteger = symbol("parseBigInteger")
val parseBigDecimal = symbol("parseBigDecimal")

object SerdeJson : RuntimeTypePackage(KotlinDependency.SERDE_JSON) {
val JsonSerialName = symbol("JsonSerialName")
val JsonSerializer = symbol("JsonSerializer")
Expand All @@ -260,8 +274,13 @@ object RuntimeTypes {
val XmlMapName = symbol("XmlMapName")
val XmlError = symbol("XmlError")
val XmlSerializer = symbol("XmlSerializer")
val XmlDeserializer = symbol("XmlDeserializer")
val XmlUnwrappedOutput = symbol("XmlUnwrappedOutput")

val XmlTagReader = symbol("XmlTagReader")
val xmlStreamReader = symbol("xmlStreamReader")
val xmlRootTagReader = symbol("xmlTagReader")
val data = symbol("data")
val tryData = symbol("tryData")
}

object SerdeFormUrl : RuntimeTypePackage(KotlinDependency.SERDE_FORM_URL) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ object KotlinTypes {
val List: Symbol = stdlibSymbol("List")
val listOf: Symbol = stdlibSymbol("listOf")
val MutableList: Symbol = stdlibSymbol("MutableList")
val MutableMap: Symbol = stdlibSymbol("MutableMap")
val Map: Symbol = stdlibSymbol("Map")
val mutableListOf: Symbol = stdlibSymbol("mutableListOf")
val mutableMapOf: Symbol = stdlibSymbol("mutableMapOf")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import software.amazon.smithy.kotlin.codegen.lang.toEscapedLiteral
import software.amazon.smithy.kotlin.codegen.model.*
import software.amazon.smithy.kotlin.codegen.rendering.serde.deserializerName
import software.amazon.smithy.kotlin.codegen.rendering.serde.formatInstant
import software.amazon.smithy.kotlin.codegen.rendering.serde.parseInstant
import software.amazon.smithy.kotlin.codegen.rendering.serde.parseInstantExpr
import software.amazon.smithy.kotlin.codegen.rendering.serde.serializerName
import software.amazon.smithy.kotlin.codegen.utils.getOrNull
import software.amazon.smithy.model.Model
Expand Down Expand Up @@ -813,14 +813,12 @@ abstract class HttpBindingProtocolGenerator : ProtocolGenerator {
HttpBinding.Location.HEADER,
defaultTimestampFormat,
)
writer
.addImport(RuntimeTypes.Core.Instant)
.write(
"builder.#L = response.headers[#S]?.let { #L }",
memberName,
headerName,
parseInstant("it", tsFormat),
)
writer.write(
"builder.#L = response.headers[#S]?.let { #L }",
memberName,
headerName,
writer.parseInstantExpr("it", tsFormat),
)
}
is ListShape -> {
// member > boolean, number, string, or timestamp
Expand Down Expand Up @@ -849,8 +847,7 @@ abstract class HttpBindingProtocolGenerator : ProtocolGenerator {
if (tsFormat == TimestampFormatTrait.Format.HTTP_DATE) {
splitFn = "splitHttpDateHeaderListValues"
}
writer.addImport(RuntimeTypes.Core.Instant)
parseInstant("it", tsFormat)
writer.parseInstantExpr("it", tsFormat)
}
is StringShape -> {
when {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -605,5 +605,3 @@ open class DeserializeStructGenerator(
}
}
}

private fun nullabilitySuffix(isSparse: Boolean): String = if (isSparse) "?" else ""
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ import software.amazon.smithy.codegen.core.CodegenException
import software.amazon.smithy.codegen.core.Symbol
import software.amazon.smithy.codegen.core.SymbolReference
import software.amazon.smithy.kotlin.codegen.KotlinSettings
import software.amazon.smithy.kotlin.codegen.core.SymbolRenderer
import software.amazon.smithy.kotlin.codegen.core.defaultName
import software.amazon.smithy.kotlin.codegen.core.*
import software.amazon.smithy.kotlin.codegen.core.mangledSuffix
import software.amazon.smithy.kotlin.codegen.model.buildSymbol
import software.amazon.smithy.model.Model
Expand Down Expand Up @@ -216,11 +215,31 @@ fun formatInstant(paramName: String, tsFmt: TimestampFormatTrait.Format, forceSt
* @param paramName The name of the local identifier to convert to an `Instant`
* @param tsFmt The timestamp format [paramName] is expected to be converted from
*/
fun parseInstant(paramName: String, tsFmt: TimestampFormatTrait.Format): String = when (tsFmt) {
TimestampFormatTrait.Format.EPOCH_SECONDS -> "Instant.fromEpochSeconds($paramName)"
TimestampFormatTrait.Format.DATE_TIME -> "Instant.fromIso8601($paramName)"
TimestampFormatTrait.Format.HTTP_DATE -> "Instant.fromRfc5322($paramName)"
else -> throw CodegenException("unknown timestamp format: $tsFmt")
fun KotlinWriter.parseInstantExpr(paramName: String, tsFmt: TimestampFormatTrait.Format): String {
val fn = when (tsFmt) {
TimestampFormatTrait.Format.EPOCH_SECONDS -> "fromEpochSeconds"
TimestampFormatTrait.Format.DATE_TIME -> "fromIso8601"
TimestampFormatTrait.Format.HTTP_DATE -> "fromRfc5322"
else -> throw CodegenException("unknown timestamp format: $tsFmt")
}
return format("#T.#L(#L)", RuntimeTypes.Core.Instant, fn, paramName)
}

fun TimestampFormatTrait.Format.toRuntimeEnum(): String = when (this) {
TimestampFormatTrait.Format.EPOCH_SECONDS -> "TimestampFormat.EPOCH_SECONDS"
TimestampFormatTrait.Format.DATE_TIME -> "TimestampFormat.ISO_8601"
TimestampFormatTrait.Format.HTTP_DATE -> "TimestampFormat.RFC_5322"
else -> throw CodegenException("unknown timestamp format: $this")
}

fun TimestampFormatTrait.Format.toRuntimeEnum(writer: KotlinWriter): String {
val enum = when (this) {
TimestampFormatTrait.Format.EPOCH_SECONDS -> "EPOCH_SECONDS"
TimestampFormatTrait.Format.DATE_TIME -> "ISO_8601"
TimestampFormatTrait.Format.HTTP_DATE -> "RFC_5322"
else -> throw CodegenException("unknown timestamp format: $this")
}
return writer.format("#T.#L", RuntimeTypes.Core.TimestampFormat, enum)
}

/**
Expand Down Expand Up @@ -289,3 +308,5 @@ internal fun Shape.childShape(model: Model): Shape? = when (this) {
is MapShape -> model.expectShape(this.value.target)
else -> null
}

internal fun nullabilitySuffix(isSparse: Boolean): String = if (isSparse) "?" else ""
aajtodd marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -680,10 +680,3 @@ open class SerializeStructGenerator(
return "serialize$suffix"
}
}

fun TimestampFormatTrait.Format.toRuntimeEnum(): String = when (this) {
TimestampFormatTrait.Format.EPOCH_SECONDS -> "TimestampFormat.EPOCH_SECONDS"
TimestampFormatTrait.Format.DATE_TIME -> "TimestampFormat.ISO_8601"
TimestampFormatTrait.Format.HTTP_DATE -> "TimestampFormat.RFC_5322"
else -> throw CodegenException("unknown timestamp format: $this")
}
Loading
Loading