Groovy HTTP Builder: Catching invalid formatted JSON response - json

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
}
}

Related

How to send JSON String with POST using ktor kotlin?

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
}

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
}
}

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)

Grails: Error when fetching and parsing JSON

I have a Grails service that sends a request to the JIRA REST API and returns JSON - When I try to use JsonSlurper to parse the JSON, I get the following error:
ERROR errors.GrailsExceptionResolver - JsonException occurred when processing request: [GET] /osmDash/jira/storyComplete
Lexing failed on line: 1, column: 1, while reading 'j', no possible valid JSON value or punctuation could be recognized.
Here is the code in the controller:
def jsonFile = jiraService.fetchJQL('issuetype=Story AND status in (Resolved,Closed,Done) AND resolved>=-30d') as JSON
def jiraSlurper = new JsonSlurper()
def jiraResult = jiraSlurper.parseText('jsonFile').total
And this is what the JSON looks like when I render it in the page:
{"total":1356,"issues":[],"startAt":0,"maxResults":0}
I was looking at groovy.json.JsonSlurper parse JSON, which seems simliar, but I couldn't get this method to work. I'm looking specifically to assign the "total" value to a variable.
This is the service that is returning the JSON:
def fetchJQL(String jql, Integer maxResults = 0, def fields = null) {
jira.request(POST, JSON) { req ->
uri.path = '/rest/api/2/search'
headers.'Authorization' = authHash
body = [jql: jql, maxResults: maxResults, fields: fields]
response.success = { resp, json ->
return json
}
response.failure = { resp ->
println resp.statusLine.statusCode
println resp.statusLine
}
}

Posting JSON data with Groovy's HTTPBuilder

I've found this doc on how to post JSON data using HttpBuilder. I'm new to this, but it is very straightforward example and easy to follow. Here is the code, assuming I had imported all required dependencies.
def http = new HTTPBuilder( 'http://example.com/handler.php' )
http.request( POST, JSON ) { req ->
body = [name:'bob', title:'construction worker']
response.success = { resp, json ->
// response handling here
}
}
Now my problem is, I'm getting an exception of
java.lang.NullPointerException
at groovyx.net.http.HTTPBuilder$RequestConfigDelegate.setBody(HTTPBuilder.java:1131)
Did I miss something? I'll greatly appreciate any help you can do.
I took a look at HttpBuilder.java:1131, and I'm guessing that the content type encoder that it retrieves in that method is null.
Most of the POST examples here set the requestContentType property in the builder, which is what it looks like the code is using to get that encoder. Try setting it like this:
import groovyx.net.http.ContentType
http.request(POST) {
uri.path = 'http://example.com/handler.php'
body = [name: 'bob', title: 'construction worker']
requestContentType = ContentType.JSON
response.success = { resp ->
println "Success! ${resp.status}"
}
response.failure = { resp ->
println "Request failed with status ${resp.status}"
}
}
I had the same problem a while ago and found a blog that noted the 'requestContentType' should be set before 'body'. Since then, I've added the comment 'Set ConentType before body or risk null pointer' in each of my httpBuilder methods.
Here's the change I would suggest for your code:
import groovyx.net.http.ContentType
http.request(POST) {
uri.path = 'http://example.com/handler.php'
// Note: Set ConentType before body or risk null pointer.
requestContentType = ContentType.JSON
body = [name: 'bob', title: 'construction worker']
response.success = { resp ->
println "Success! ${resp.status}"
}
response.failure = { resp ->
println "Request failed with status ${resp.status}"
}
}
Cheers!
If you need to execute a POST with contentType JSON and pass a complex json data, try to convert your body manually:
def attributes = [a:[b:[c:[]]], d:[]] //Complex structure
def http = new HTTPBuilder("your-url")
http.auth.basic('user', 'pass') // Optional
http.request (POST, ContentType.JSON) { req ->
uri.path = path
body = (attributes as JSON).toString()
response.success = { resp, json -> }
response.failure = { resp, json -> }
}
I found an answer in this post: POST with HTTPBuilder -> NullPointerException?
It's not the accepted answer, but it worked for me. You may need to set the content type before you specify the 'body' attribute. It seems silly to me, but there it is. You could also use the 'send contentType, [attrs]' syntax, but I found it more difficult to unit test. Hope this helps (late as it is)!
I gave up on HTTPBuilder in my Grails application (for POST at least) and used the sendHttps method offered here.
(Bear in mind that if you are using straight Groovy outside of a Grails app, the techniques for de/encoding the JSON will be different to those below)
Just replace the content-type with application/json in the following lines of sendHttps()
httpPost.setHeader("Content-Type", "text/xml")
...
reqEntity.setContentType("text/xml")
You will also be responsible for marshalling your JSON data
import grails.converters.*
def uploadContact(Contact contact){
def packet = [
person : [
first_name: contact.firstName,
last_name: contact.lastName,
email: contact.email,
company_name: contact.company
]
] as JSON //encode as JSON
def response = sendHttps(SOME_URL, packet.toString())
def json = JSON.parse(response) //decode response
// do something with json
}