How to send JSON String with POST using ktor kotlin? - json

How to add JSON String in the POST request using kotlin and ktor?
Printing it out the Json string read from file or even constructed string with Kotlin in the client, the content looks like JSON.
Still, the server cannot recognize the string as JSON, and when I print it in the server, each double quota is back slashed.
The client obviously adds the back slashes, thus the request is not formatted as it should.
Client Kotlin - Ktor code:
import com.google.gson.*
import io.ktor.client.*
import io.ktor.http.*
...
val client = HttpClient(OkHttp) {
install(JsonFeature) {
serializer = GsonSerializer()
}
}
val fileContent = MyClass::class.java.getResource("myfile").readText()
println("fileContent string = $fileContent")
val out = client.post<String> {
url(url)
contentType(ContentType.Application.Json)
body = fileContent
}
the print out looks like this :
{ "name": "myname", "value": "myvalue" }
but the server (I use hookbin by the way to really print out the data without Jackson conversions) prints out:
{ \"name\": \"myname\", \"value\": \"myvalue\" }

The solution is to pass to the HTTP POST request a JSON object not a String object. They look the same when you print them, but of course they are not equally interpreted by the JVM. So, we have just to parse the string. I use the GSON library.
Add the JsonParser -> parseString method and use its object in the client:
import com.google.gson.JsonParser
val fileContent = MyClass::class.java.getResource("myfile").readText()
println("fileContent string = $fileContent")
var bodyAsJsonObject = JsonParser.parseString(fileContent).asJsonObject
println("bodyAsJsonObject = $bodyAsJsonObject")
val out = client.post<String> {
url(url)
contentType(ContentType.Application.Json)
body = bodyAsJsonObject
}

Related

Decoding json from google firebase rest api using Dart, not Flutter

I can retrieve a list of documents from a collection in a Cloud Firestore instance, in Firebase. The response contains the most verbose json I have ever seen. Here is a taste, ...
{
documents: [
{
name: projects/myprojectId/databases/(default)/documents/mycollection/0HC2spBFxEMNUc8VQLFg,
fields: {
name: {
stringValue: Jim's Bait Shop},
taxId: {
stringValue:
},
mailingAddress: {
mapValue: {
fields: {
streetAddress1: {
stringValue:
}
},
streetAddress2: {
stringValue:
},
state: {
stringValue: NC
},
city: {
stringValue: Boone
},
zipCode: {
stringValue:
}
}
}
}
},
createTime: 2020-08-31T19
:
54: 28.643464Z,
updateTime: 2020-09-01T02
:
35: 08.203028Z
},
{ ...
When trying to use jsonDecode, in dart:convert, it fails to de-serialize the json response into a collection of Dart objects.
'_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'String'
And if I use cUrl instead of Dart, the json response looks just as verbose.
I'm using the FirebaseClient in "package:firebase/firebase_io.dart" to authenticate and read the collection.
I tried to build a "reviver" function but jsonDecode would not accept it so I'm not sure how I messed that up.
Anyway, I'm not seeing much guidance in the documentation on how to marshal this verbose json response into Dart objects. I suspect this server-side Dart is somewhat new territory. I want to avoid packages that require Flutter because I'm using a prebuilt docker image, with the Dart runtime preinstalled, on Google Cloud Run. (Truthfully, I've already tried a few Flutter packages for Firestore and a Flutter docker image.) I'll take any suggestions you have.
Below is the file I've been using for testing.
import 'package:firebase/firebase_io.dart';
import 'credentials.dart'; // borrowed from a SO post
import 'dart:convert';
const base = 'https://firestore.googleapis.com/v1/projects/';
void main() async {
// get private key...
final credential = await Credentials.fetch(); // string
final fbClient = FirebaseClient(credential);
final path = base + 'my_project_id/databases/(default)/documents/my_collection'
'?mask.fieldPaths=name&mask.fieldPaths=taxId&mask.fieldPaths=mailingAddress&orderBy=orgId';
final response = await fbClient.get(path);
print(response);
final orgs = jsonDecode(response); // unhandled exception
fbClient.close();
}
I think I might need to switch to a more sophisticated json deserializer package, and annotate my model classes to explicitly map this gnarly json to specific Dart class properties. But I have not yet seen a Dart package that supports such capabilities.
I have tried to use "json_serializable: 3.4.1" but failed to get code generation to work.
An online json validator is saying the response is malformed due to an apostrophe but can I trust that? Doubt I can escape special chars.
The error message says that response is not a String, it's a Map.
That means that Firebase has already parsed the JSON for you and returns the parsed structure.
You don't need to use jsonDecode, just final orgs = response;.
The solution was to stop using FirebaseClient, because it was not wrapping the name-value pairs in double quotation marks. Just use normal http instead.
import 'package:http/http.dart' as http;
const base = 'https://firestore.googleapis.com/v1/projects/';
void main() async {
//
final uri = base + 'myproject/databases/(default)/documents/mycollection' +
'?mask.fieldPaths=name&mask.fieldPaths=taxId&mask.fieldPaths=mailingAddress&orderBy=orgId&alt=json';
var response = await http.get(uri);
// print(response.body);
var json = jsonDecode(response.body)['documents'] as List;
List l = json.map((o) => MyModelClass.fromJson(o)).toList();
https://pub.dev/packages/http

Getting raw json string or customer jackson mapper from httpbuilder-ng get request

I'm updating groovy scripts from httpbuilder to httpbuilder-ng. These scripts interact with various webservices to get messages. I'm looking to parse the responses preferably using Jackson into objects. However, I can't seem to get the raw json response like I could in httpbuilder as httpbuilder-ng auto parses into a lazymap in all cases.
The old implementation using httpbuilder allowed you to use the body.text method to get the raw json string without it being parsed into a lazymap. This could then be used with ObjectMapper in Jackson to create my POJO's. However, httpbuilder-ng doesn't seem to support this.
I have already tried the method here for getting the raw json, but the body.text method does not seem to work in version 1.03 that I'm using http://coffeaelectronica.com/blog/2016/httpbuilder-ng-demo.html.
I have also tried to add my own custom encoders to override Groovy's default creation of JSON objects without any success. It is supposedly possible as detailed in the wiki https://http-builder-ng.github.io/http-builder-ng/asciidoc/html5/. If anyone has a code snippet of how to do this it would be appreciated!
Old httpbuilder code:
def http = new HTTPBuilder("http://${proxy}.domain.ie")
http.request(GET, TEXT) {
uri.path = "/blah/plugins/blah/queues"
headers.Accept = 'application/json'
headers.'Cookie' = "token=${token}"
response.success = { resp, json ->
assert resp.status < 300
LOGGER.info("GET queues request succeeded, HTTP " + resp.status)
ObjectMapper objectMapper = new ObjectMapper()
queues = objectMapper.readValue(json, Queue[].class)
}
response.failure = { resp ->
assert resp.status >= 300
LOGGER.info("GET queues request failed, HTTP " + resp.status)
return null
}
}
new http-builder-ng code:
def http = configure {
request.uri = "http://${proxy}.domain.ie"
request.headers["Accept"] = "application/json"
request.headers["Cookie"] = "token=${token}"
request.contentType = TEXT
}
return http.get {
request.uri.path = "/blah/plugins/blah/queues"
response.success { FromServer fs, Object body ->
return body.text // doesn't work
}
response.failure {
return null
}
}
Update
Found the solution. It was to add a custom parser and a closure using the FromServer.inputstream.text method.
def text = httpBuilder.get {
request.uri.path = '/blah/plugins/blah/queues'
response.parser('application/json') { ChainedHttpConfig cfg, FromServer fs ->
String text = fs.inputStream.text
}
}

how can I get JSON from HTTP request as a String and not as JsValue

I am playing around with Json object. Is there a way to get the Json passed in HTTP request as String and not as JsValue? I want to use Json object to parse it instead of using asJson.
def registrationRequest = Action { request => {
Logger.debug("received message:"+request)
Logger.debug("received message:"+request.body)
val jsv:JsValue = Json.parse**(/* I want to get json as string here from request's body*/)**
(jsv \ "first-name").asOpt[String].map(nameValue=>{
Logger.debug("name is "+nameValue); Ok(nameValue)
}).getOrElse{BadRequest("no name")}
}
}
You can get raw text from JSON request by using tolerantText parser:
def index = Action(parse.tolerantText) { request =>
val jsonAsString = request.body
}

Groovy HTTP Builder: Catching invalid formatted JSON response

Hi I'm using Groovy HTTPBuilder to make a POST call similar like this:
http.request( POST, JSON ) { req ->
body = [name:'testName', title:'testTitle']
response.success = { resp, json ->
println resp.statusLine
println json
}
}
However due to a Bug (which I cannot solve myself), the REST server returns a JSON that is not correctly formatted resulting in the following exception when my application is trying to parse it:
groovy.json.JsonException: Unable to determine the current character, it is not a string, number, array, or object
I'm fairly new to groovy closures and HTTPBuilder, but is there a way to make the application check if the JSON is actually valid before parsing it and returning null if so?
I'm not sure this is a good idea, but it is possible to supply your own JSON parser, which facilitates the original request. It also affords the opportunity to "fix" the JSON if the known bug can be fixed.
Example below. The parser is essentially the same code that HTTPBuilder uses, with the exception of a hard-coded charset of UTF-8.
#Grab(group='org.codehaus.groovy.modules.http-builder', module='http-builder', version='0.7' )
import groovy.json.*
import groovyx.net.http.*
import static groovyx.net.http.Method.*
import static groovyx.net.http.ContentType.*
def url = "http://localhost:5150/foobar/bogus.json"
def http = new HTTPBuilder(url)
http.parser."application/json" = { resp ->
println "tracer: custom parser"
def text = new InputStreamReader( resp.getEntity().getContent(), "UTF-8");
def result = null
try {
result = new JsonSlurper().parse(text)
} catch (Exception ex) {
// one could potentially try to "fix" the JSON here if
// there is a known bug in the server
println "warn: custom parser caught exception"
}
return result
}
http.request( POST, JSON ) { req ->
body = [name:'testName', title:'testTitle']
response.success = { resp, json ->
println resp.statusLine
println json
}
}

Groovy returning JSON

I have the following Groovy script (not a Grails app) that is returning a JSON-like, but it is not strictly valid JSON.
String baseURL = 'https://test.com'
File userFile = new File("./user.json")
def client = new HTTPBuilder(baseUrl)
client.headers['Content-Type'] = 'application/json'
client.request(GET, JSON) { req ->
requestContentType = JSON
headers.Accept = 'application/json'
response.success = { resp, json ->
userFile.append json.toString()
println JsonOutput.toJson(json.toString())
}
}
I am trying to create a JSON output file. I have tried using JsonOutput.prettyPrint and I looked at JsonBuilder, but that looks like I would have to build the JSON structure manually when Groovy should support the output. This is what I am getting back.
{AssetNumber=AssetNumber1, DeviceFriendlyName=FriendlyName1, PhoneNumber=17035551231, SerialNumber=SerialNumber1, Udid=Udid1, UserEmailAddress=user1#email.com, UserId=userId1, UserName=userName1}
As I said, this is JSON-like, but not strictly valid. I was expecting something like:
{"AssetNumber": "AssetNumber1", "DeviceFriendlyName": "FriendlyName1"....}
Any ideas?
It works perfectly fine (groovy v 2.3.6):
import groovy.json.*
def pretty = JsonOutput.prettyPrint(JsonOutput.toJson([1:2]))
assert pretty == """{
"1": 2
}"""
In this closure:
response.success = { resp, json ->
userFile.append json.toString()
println JsonOutput.toJson(json.toString())
}
You're getting an instance of Map under json variable. You do not need to turn it into a string. Instead use:
userFile.append JsonOutput.toJson(json)
println JsonOutput.toJson(json)