ktor dynamic keys serialization and json preprosesing - json-deserialization

Is there anyway in Ktor to alter a json before deserialization process? In the example below there is a json with dynamic keys in which i need to remove the "token" key as it is of a string type instead of an object type as the other keys.
When i urn the code i get the following error:
Exception in thread "main" kotlinx.serialization.json.internal.JsonDecodingException: Expected class kotlinx.serialization.json.JsonObject as the serialized body of shell.remoting.Market, but had class kotlinx.serialization.json.JsonLiteral
I'm not sure if there is another better way to do it. Any suggestion will be appreciated, thanks
object MarketMapSerializer :
JsonTransformingSerializer<Map<String, Market>>(MapSerializer(String.serializer(), Market.serializer())) {
override fun transformSerialize(element: JsonElement): JsonElement =
JsonObject(element.jsonObject.filterNot { (k, _) ->
k == "token"
})
}
#Serializable
data class Market(
val stations: List<Station>,
)
#Serializable
data class Station(
#JsonNames("iata_code")
val iataCode: String,
val destinations: List<String>,
)
fun main() {
val jsonString = """
{
"Republica Dominicana": {
"stations": [
{
"iata_code": "PUJ",
"destinations": [
"ADZ",
"BAQ",
"VVC"
]
}
]
},
"Brasil": {
"stations": [
{
"iata_code": "AJO",
"destinations": [
"ADZ",
"BAQ",
"VVC"
]
}
]
},
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkb3RSRVogQVBJIiwianRpIjoiYTVhZDM4NmYtOGViNy0yOWQ5LWZkMGYtM2Q3MzQwZmRhOGI2IiwiaXNzIjoiZG90UkVaIEFQSSJ9.V2YfXCt9r7Tzae9SYSoj-qIyxjRc9YoE2XPoIQQNI9U"
}
""".trimIndent()
println(
Json.decodeFromString(
MarketMapSerializer,
jsonString
)
)
}

Just replace transformSerialize with the transformDeserialize since you're doing deserialization.

Related

Invalid Expression exception for JsonPath in RestAssured

We are using restassured for API automation in our project . I have sample response for which I tested JsonPath expression on https://www.jsonquerytool.com/. My Json expression is - $[*]['tags'][?(#.id==0)].
I am getting proper output when I tried expression on JsonQuerytool. But when I try same in below code , I get invalid expression message -
JsonPath jsonPathEvaluator = response.jsonPath();
ArrayList result = jsonPathEvaluator.get("$[*]['tags'][?(#.id==0)]");
Above code throws exception .
Can anyone tell me how can I programmatically query the response using JsonPathEvaluator ?
P.S - Response not pasted as it was very huge.
Since your issue is not in any particular query (expression does not matter) I'm answering using example from the link you provided.
RestAssured uses some special JsonPath library (which uses its own syntax in some cases) called GPath. So if you have json like this:
{
"key": "value",
"array": [
{
"key": 1
},
{
"key": 2,
"dictionary": {
"a": "Apple",
"b": "Butterfly",
"c": "Cat",
"d": "Dog"
}
},
{
"key": 3
}
]
}
And expect you would use query like this: $.array[?(#.key==2)].dictionary.a
Then for RestAssured case your query would be like this: array.findAll{i -> i.key == 2}.dictionary.a
So the complete code example would be:
public static void main(String[] args) {
JsonPath jsonPath = RestAssured
.get("http://demo1954881.mockable.io/gath")
.jsonPath();
List<String> resp = jsonPath.get("array.findAll{i -> i.key == 2}.dictionary.a");
System.out.println(resp);
}

Headers are not being shown in Swagger API documentation for Ktor API (Kotlin)

I'm using the papsign/Ktor-OpenAPI-Generator to generate Swagger API documentation for a Ktor application. I have a POST endpoint which contains headers in the request. Here is the entire code:
fun main(args: Array<String>): Unit =
io.ktor.server.netty.EngineMain.main(args)
#Suppress("unused")
fun Application.module() {
install(OpenAPIGen) {
info {
version = "1.0.0"
title = "Accelerated Earn"
description = "Accelerated earn service provides extra earn."
contact {
name = "John Doe"
email = "johndoe#gmail.com"
}
}
// describe the servers, add as many as you want
server("/") {
description = "This server"
}
replaceModule(DefaultSchemaNamer, object: SchemaNamer {
val regex = Regex("[A-Za-z0-9_.]+")
override fun get(type: KType): String {
return type.toString().replace(regex) { it.value.split(".").last() }.replace(Regex(">|<|, "), "_")
}
})
}
// Configuring jackson serialization
install(ContentNegotiation) {
jackson {
dateFormat = DateFormat.getDateTimeInstance()
}
}
// Configuring swagger routes
routing {
get("/openapi.json") {
call.respond(application.openAPIGen.api.serialize())
}
get("/") {
call.respondRedirect("/swagger-ui/index.html?url=/openapi.json", true)
}
}
demoFunction()
}
// Defining the DEMO tag
enum class Tags(override val description: String) : APITag {
DEMO("This is a demo endpoint")
}
// Request is of type DemoClass, response is Boolean
fun Application.demoFunction() {
apiRouting {
tag(Tags.DEMO) {
route("/demo") {
post<Unit, Boolean, DemoClass>(
status(HttpStatusCode.OK),
info(
"Demo endpoint",
"This is a demo API"
),
exampleResponse = true,
exampleRequest = DemoClass(name = "john doe", xyz = "some header")
) { _, request ->
respond(true)
}
}
}
}
}
#Request("Request")
data class DemoClass(
#HeaderParam("Sample header")
val xyz: String,
#PathParam("Name")
val name: String
)
In DemoClass, I have used #HeaderParam annotation to denote "xyz" property as a header, and used #PathParam annotation to denote "name" as a parameter of the request. I expected that "xyz" would be shown as a header in the documentation but it is being shown as a part of the request body, and nothing about headers is mentioned (as shown in the below figures)
Because of this, while making a request, I have to put the header inside the request body instead of passing it as a header in the request. Is it possible to fix this? How do I do it?
Found the error. The first parameter of post function is the class of header. Here is the updated code :-
fun Application.demoFunction() {
apiRouting {
tag(Tags.DEMO) {
route("/demo") {
post<Headers, Boolean, DemoClass>(
status(HttpStatusCode.OK),
info(
"Demo endpoint",
"This is a demo API"
),
exampleResponse = true,
exampleRequest = DemoClass(name = "john doe")
) { header, request ->
respond(true)
}
}
}
}
}
#Request("Request")
data class DemoClass(
#PathParam("Name")
val name: String
)
data class Headers(
#HeaderParam("Sample Header")
val xyz: String
)

How to verify types from response?

I have to check json response if it contains needed types. What do I mean:
{
"success": {
"anonym_id": 11156,
"has_empty_profile": false,
"token": "4690e404-cfec-4918-b555-2f0d84675eee",
"twins": [],
"uid": 7380
}
}
So I have to check that "anonym_id" is int(not a specific number like here 11156, but just int), "has_empty_profile" is boolean, "token" is string etc.
How to do that with rest assured?
Best way to do this is
JsonPath js1 = new JsonPath(new File(
"Your Json I stored in a file"));
assertTrue(js1.get("success.findAll {anonym_id -> anonym_id=11156}.has_empty_profile") instanceof Boolean);
assertTrue(js1.get("success.findAll {anonym_id -> anonym_id=11156}.token") instanceof String);

Parsing boto3 invoke_endpoint response from AWS SageMaker

I have a Sagemaker endpoint that I can infer to from boto3 client and get response.
Per boto3 doc, the Body of the response result is a Byte object StreamingBody type. I convert it to a dictionary
response = client.invoke_endpoint(EndpointName=endpoint_name, Body=json.dumps(data))
response_body = response['Body']
dict_response = response_body.read().decode('utf-8')
print(dict_response)
The above code gives me a response like below (stripped down for this post)
I need to retrieve the array from the "floatVal" key. How do I do that?
{
"outputs": {
"score": {
"dtype": "DT_FLOAT",
"floatVal": [
0.00012408883776515722,
...........
-0.8316119909286499,
-0.24423488974571228
],
"tensorShape": {
"dim": [
{
"size": "1"
},
{
"size": "1024"
}
]
}
}
},
"modelSpec": {
"version": "1",
"name": "generic_model",
"signatureName": "serving_default"
}
}
Actually the dict_response is not really a dictionary here, rather a string type. So I had to convert the dict_response to an actual dictionary and then I could retrieve the floatVal key.
Updated code
response = client.invoke_endpoint(EndpointName=endpoint_name, Body=json.dumps(data))
response_body = response['Body']
response_str = response_body.read().decode('utf-8')
response_dict = eval(response_str)
print(response_dict['outputs']['score']['floatVal'])

Looping in a C-style For loop instead of using each

how can i loop through a json file using a FOR loop in groovy? I am able to do it with .each but i am in a situation/bug where i cannot use .each loops.
The json file is being read and parsed into an object.
The json looks like this:
{
"workflows1": {
"name": "/wf_multifolder",
"file": "release1/wf_multifolder.XML",
"folderNames": {
"multifolder": "{{multifolder}}",
"agent1": "{{agentx}}"
}
},
"workflows2": {
"name": "/wf_multifolder",
"file": "release1/wf_multifolder.XML",
"folderNames": {
"multifolder": "{{multifolder}}",
"agent1": "{{agentx}}"
}
}
}
Note: i can modify the json file, if need to make the process simpler.. All i am try to do is to loop throgh and extract the values for the keys.
So given the json in a String like so:
def jsonText = '''{
"workflows1": {
"name": "/wf_multifolder",
"file": "release1/wf_multifolder.XML",
"folderNames": {
"multifolder": "{{multifolder}}",
"agent1": "{{agentx}}"
}
},
"workflows2": {
"name": "/wf_multifolder",
"file": "release1/wf_multifolder.XML",
"folderNames": {
"multifolder": "{{multifolder}}",
"agent1": "{{agentx}}"
}
}
}'''
You can just do:
import groovy.json.*
def json = new JsonSlurper().parseText(jsonText)
for(entry in json) {
println "$entry.key has file $entry.value.file"
}
to print:
workflows1 has file release1/wf_multifolder.XML
workflows2 has file release1/wf_multifolder.XML

Resources