v79
01/27/2023, 8:52 AM@Serializable
data class ContainsJsonString(val name: String, val jsonString: String)
Is there some annotation which will let me tell kotlinx not to escape or otherwise mess about with the jsonString
property? I don't need to worry about deserialization, only interested in writing the class `ConstainsJsonString`to a json file. Output would be something like:
{
"name": "Liam",
"jsonString": {
"address": "1 Acacia Avenue",
"favouriteColour": "blue"
}
}
Right now I'm getting something like:
{
"name": "Liam",
"jsonString": "{ \\\"address\\\" : \\\"1 Acacia Avenue\\\":\\\"favouriteColour\\\":\\\"blue\\\"}"
}
(Posted in this the wrong channel earlier, sorry!)Adam S
01/27/2023, 9:07 AMAdam S
01/27/2023, 9:08 AMJsonTransformingSerializer
https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-transforming-serializer/v79
01/27/2023, 9:14 AMAdam S
01/27/2023, 9:19 AMAdam S
01/27/2023, 9:20 AM@Serializable
data class ContainsJsonString(val name: String, val jsonString: RawJsonString)
Adam S
01/27/2023, 9:20 AMv79
01/27/2023, 9:23 AMResult
sealed class with some status, etc values. I tried loading the json, deserializing that, adding the object to the Result class, and then serializing everything, but that went horribly wrong and produced invalid JSON. As this is just one-way, I thought I could just return the JSON file content directly as a String in the Result sealed class.v79
01/27/2023, 9:27 AMAdam S
01/27/2023, 9:27 AMAdam S
01/27/2023, 9:28 AMv79
01/27/2023, 9:29 AMv79
01/27/2023, 9:45 AMJsonUnquotedLiteral
code, I've written a test case which is failing.
@Test
fun `can serialize an API JsonSuccess result containing a literal json string`() {
val expected = """
{"jsonString":{"name":"Json Literal","count":1}}
""".trimIndent()
val jsonObject = WillBeJson(name = "Json Literal", count = 1)
val jsonObjectString = Json.encodeToString(jsonObject)
val jsonSuccess = APIResult.JsonSuccess(jsonString = jsonObjectString)
val result = Json.encodeToString(jsonSuccess)
println(result)
assertEquals(expected, result)
}
Test fails:
Expected :{"jsonString":{"name":"Json Literal","count":1}}
Actual :{"jsonString":"{\"name\":\"Json Literal\",\"count\":1}"}
Adam S
01/27/2023, 9:49 AMJsonUnquotedLiteral
needs to be used before the content is converted into a JsonPrimitive
(I think). OR maybe the typealias
doesn’t work for primitives. Or both!Adam S
01/27/2023, 9:50 AMv79
01/27/2023, 9:51 AMAdam S
01/27/2023, 9:55 AMAdam S
01/27/2023, 10:09 AM@Serializable(with = RawJsonStringSerializer::class)
to the property.
If you have lots of properties I’d recommend using the value class RawJsonString(...)
method, which reduces the annotation spam.
I’m not sure why the typealias
doesn’t work - I’ll make an issue. EDIT: this is a known issue that will be fixed in 1.8.20, and might be backported to 1.8…?v79
01/27/2023, 10:11 AMv79
01/27/2023, 11:12 AMAdam S
01/27/2023, 11:34 AMv79
01/27/2023, 2:44 PM{"statusCode":200,"headers":{"Content-Type":"application/json"},"body":"{\"jsonString\":{\r\n \"layouts\": .... // rest of my raw json here
So a couple of things: the body is still being escaped, but I think that's actually AWS API Gateway doing this, because an APIGatewayProxyResponseEvent's body should be escaped to a single string, not a Json entity.
But what I think is wrong is the appearance of jsonString
in the body. That's coming from the serializer surrogate which isn't really understanding that this is a value class. From the docs, I think I want the RawJsonStringSerializer to call something like encoder.encodeInlineElement().encodeJsonElement(JsonUnquotedLiteral(value.content))
but I know that isn't how these things work. Anyway, progress, and I could live with it.v79
01/27/2023, 10:01 PMAdam S
01/27/2023, 10:36 PMbody
property? I don’t see any property named body
in your code. But there is a jsonString
property (in the APIResultSurrogate
class), so it looks correct that resulting JSON has a jsonString
propertyv79
01/27/2023, 10:48 PMbody
and other related parts come from). I mostly understand why I get the result I get, I just hoped the value class would give me the result I hoped for. If I strip away the sealed class and the surrogate, it works with the value class.snowe
01/25/2024, 11:31 PM@Serializable
sealed class LambdaProxyRequest<T>(
val version: String? = null,
val routeKey: String? = null,
val rawPath: String? = null,
val rawQueryString: String? = null,
val cookies: List<String>? = null,
val headers: Headers? = null,
val queryStringParameters: QueryStringParameters? = null,
val requestContext: RequestContext? = null,
val body: T? = null,
val pathParameters: PathParameters? = null,
val isBase64Encoded: Boolean? = null,
val stageVariables: StageVariables? = null,
)
object Request1: LambdaProxyRequest<CalculateRequest>()
object Request2: LambdaProxyRequest<DoOtherStuffRequest>()
where the body
actually comes across the wire as an escaped JSON string, but I want to deserialize it as the generic object, rather than manually pull out the escaped string, then unescape it, then decode it again. I don’t exactly see how the Json primitive stuff y’all talked about above would solve this, but I do see how it could be done with a custom deserializer that simply searches each index until it finds the body
and then unescapes it, then finally deserializes the whole object as if nothing was changed. But I’m having trouble implementing that. Am I off base here? Is it not possible to do this?snowe
01/26/2024, 12:09 AMobject SomeProxyDeserializer: KSerializer<Some> {
fun unescapeJson(escapedJson: String): String {
return escapedJson.replace("""\"""", "\"")
}
override val descriptor: SerialDescriptor
get() = PrimitiveSerialDescriptor("body", PrimitiveKind.STRING)
override fun deserialize(decoder: Decoder): Some {
val unescapedString = unescapeJson(decoder.decodeString())
return Json.decodeFromString(Some.serializer(), unescapedString)
}
override fun serialize(encoder: Encoder, value: Some) {
TODO("No need to ever serialize this, probably. XD")
}
}
but it doesn’t use the original Json object that was used to start the deserialization process, thus immediately failing as some of the serialization modules aren’t available.snowe
01/26/2024, 12:10 AMAdam S
01/26/2024, 12:11 AMsnowe
01/26/2024, 12:12 AMsnowe
01/26/2024, 12:14 AM