I have written some code in Kotlin that should retrieve some data for a dictionary app using the JSON Request Object. I can see that the call is made successfully. The website receiving the call shows the data being sent back but I'm not getting anything back in the results object. Logcat is showing this error (E/JSONĀ ERROR: No value for results). I'm not sure where I'm going wrong in extracting the results. Can someone point me in the right direction?
val jsonObjectRequest = JsonObjectRequest(Request.Method.GET, url, null,
{ response ->
try {
val resultsObj = response.getJSONObject("results")
val result: JSONObject = response.getJSONObject("result")
val term = result.getString("term")
val definition = result.getString("definition")
val partOfSpeech = result.getString("partOfSpeech")
val example = result.getString("example")
} catch (ex: JSONException) {
Log.e("JSON ERROR", ex.message!!)
}
},
{ error: VolleyError? -> error?.printStackTrace() })
The JSON
{
"results": {
"result": {
"term": "consistent, uniform",
"definition": "the same throughout in structure or composition",
"partofspeech": "adj",
"example": "bituminous coal is often treated as a
consistent and homogeneous product"
}
}
}
Have you checked the json format? Json Formatter
Here with this code it is valid. You had his character end line in the wrong place.
{
"results":{
"result":{
"term":"consistent, uniform",
"definition":"the same throughout in structure or composition",
"partofspeech":"adj",
"example":"bituminous coal is often treated as a consistent and homogeneous product"
}
}
}
While trying to download and parse JSON files through Kotlin, it kept failing trying to access the document, though trying different (shorter) URLS seemed to work fine
val string = "http://ddragon.leagueoflegends.com/cdn/11.2.1/data/en_GB/champion.json"
val client = OkHttpClient()
val request = Request.Builder().url(string).build()
client.newCall(request).enqueue(object: Callback
{
override fun onResponse(call: Call, response: Response) {
val body = response.body?.string()
println(body)
}
override fun onFailure(call: Call, e: IOException) {
println("Failed")
}
})
This gives the "Failed" output in the console:failed#1
However, using a shorter URL such as this:
val string = "https://ddragon.leagueoflegends.com/api/versions.json"
Gives the correct output: working #1
Anyone know why and/or a fix to this?
Thanks!
Update:
Trying with a file that is considerably smaller than the first, but includes two directories instead of one:
val string = "http://static.developer.riotgames.com/docs/lol/maps.json"
Still ends up failing leading me to believe it is unable to access the document if it is too nested within directories?
I use http4s and scalaz.steam._
def usersService = HttpService{
case req # POST -> Root / "check" / IntVar(userId) =>{
//val jsonobj=parse(req.as[String])
val taskAsJson:String = write[req.body]
Ok(read[Task](taskAsJson))
}
}
For http4s, the request can get body as the type Process[Task, ByteVector]
The Process is scalaz.stream.process class. You can find details on scalaz.
I want to write a Task here can deal with JSON ByteVector and translate into a Map (like HashMap), layer by layer.
I mean, for example:
{
"id":"12589",
"customers":[{
"id": "898970",
"name":"Frank"
...
},
]
}
I do not know how to write the function via scalaz.stream.Process to change the JSON to mapobject.
I want response and to parse the JSON object, returning another refactored JSON object; how can I do it?
Is it possible to dynamically deserialize an external, of unknown length, ByteString stream from Akka HTTP into domain objects?
Context
I call an infinitely long HTTP endpoint that outputs a JSON Array that keeps growing:
[
{ "prop": true, "prop2": false, "prop3": 97, "prop4": "sample" },
{ "prop": true, "prop2": false, "prop3": 97, "prop4": "sample" },
{ "prop": true, "prop2": false, "prop3": 97, "prop4": "sample" },
{ "prop": true, "prop2": false, "prop3": 97, "prop4": "sample" },
{ "prop": true, "prop2": false, "prop3": 97, "prop4": "sample" },
...
] <- Never sees the daylight
I guess that JsonFraming.objectScanner(Int.MaxValue) should be used in this case. As docs state:
Returns a Flow that implements a "brace counting" based framing
operator for emitting valid JSON chunks. It scans the incoming data
stream for valid JSON objects and returns chunks of ByteStrings
containing only those valid chunks. Typical examples of data that one
may want to frame using this operator include: Very large arrays
So you can end up with something like this:
val response: Future[HttpResponse] = Http().singleRequest(HttpRequest(uri = serviceUrl))
response.onComplete {
case Success(value) =>
value.entity.dataBytes
.via(JsonFraming.objectScanner(Int.MaxValue))
.map(_.utf8String) // In case you have ByteString
.map(decode[MyEntity](_)) // Use any Unmarshaller here
.grouped(20)
.runWith(Sink.ignore) // Do whatever you need here
case Failure(exception) => log.error(exception, "Api call failed")
}
I had a very similar problem trying to parse the Twitter Stream (an infinite string) into a domain object.
I solved it using Json4s, like this:
case class Tweet(username: String, geolocation: Option[Geo])
case class Geo(latitude: Float, longitude: Float)
object Tweet{
def apply(s: String): Tweet = {
parse(StringInput(s), useBigDecimalForDouble = false, useBigIntForLong = false).extract[Tweet]
}
}
Then I just buffer the stream and mapped it to a Tweet:
val reader = new BufferedReader(new InputStreamReader(new GZIPInputStream(inputStream), "UTF-8"))
var line = reader.readLine()
while(line != null){
store(Tweet.apply(line))
line = reader.readLine()
}
Json4s has full support over Option (or custom objects inside the object, like Geo in the example). Therefore, you can put an Option like I did, and if the field doesn't come in the Json, it will be set to None.
Hope it helps!
I think that play-iteratees-extras must help you. This library allow to parse Json via Enumerator/Iteratee pattern and, of course, don't waiting for receiving all data.
For example, lest build 'infinite' stream of bytes that represents 'infinite' Json array.
import play.api.libs.iteratee.{Enumeratee, Enumerator, Iteratee}
var i = 0
var isFirstWas = false
val max = 10000
val stream = Enumerator("[".getBytes) andThen Enumerator.generateM {
Future {
i += 1
if (i < max) {
val json = Json.stringify(Json.obj(
"prop" -> Random.nextBoolean(),
"prop2" -> Random.nextBoolean(),
"prop3" -> Random.nextInt(),
"prop4" -> Random.alphanumeric.take(5).mkString("")
))
val string = if (isFirstWas) {
"," + json
} else {
isFirstWas = true
json
}
Some(Codec.utf_8.encode(string))
} else if (i == max) Some("]".getBytes) // <------ this is the last jsArray closing tag
else None
}
}
Ok, this value contains jsArray of 10000 (or more) objects. Lets define case class that will be contain data of each object in our array.
case class Props(prop: Boolean, prop2: Boolean, prop3: Int, prop4: String)
Now write parser, that will be parse each item
import play.extras.iteratees._
import JsonBodyParser._
import JsonIteratees._
import JsonEnumeratees._
val parser = jsArray(jsValues(jsSimpleObject)) ><> Enumeratee.map { json =>
for {
prop <- json.\("prop").asOpt[Boolean]
prop2 <- json.\("prop2").asOpt[Boolean]
prop3 <- json.\("prop3").asOpt[Int]
prop4 <- json.\("prop4").asOpt[String]
} yield Props(prop, prop2, prop3, prop4)
}
Please, see doc for jsArray, jsValues and jsSimpleObject. To build result producer:
val result = stream &> Encoding.decode() ><> parser
Encoding.decode() from JsonIteratees package will decode bytes as CharString. result value has type Enumerator[Option[Item]] and you can apply some iteratee to this enumerator to start parsing process.
In total, I don't know how you receive bytes (the solution depends heavily on this), but I think that show one of the possible solutions of your problem.
I have the below JSON string coming in as a request parameter into my grails controller.
{
"loginName":"user1",
"timesheetList":
[
{
"periodBegin":"2014/10/12",
"periodEnd":"2014/10/18",
"timesheetRows":[
{
"task":"Cleaning",
"description":"cleaning description",
"paycode":"payCode1"
},
{
"task":"painting",
"activityDescription":"painting description",
"paycode":"payCode2"
}
]
}
],
"overallStatus":"SUCCESS"
}
As you can see, the timesheetList might have multiple elements in it. In this ( above ) case, we have only one. So, I expect it to behave like an Array/List.
Then I had the below code to parse through it:
String saveJSON // This holds the above JSON string.
def jsonObject = grails.converters.JSON.parse(saveJSON) // No problem here. Returns a JSONObject. I checked the class type.
def jsonArray = jsonArray.timesheetList // No problem here. Returns a JSONArray. I checked the class type.
println "*** Size of jsonArray1: " + jsonArray1.size() // Returns size 1. It seemed fine as the above JSON string had only one timesheet in timesheetList
def timesheet1 = jsonArray[1] // This throws the JSONException, JSONArray[1] not found. I tried jsonArray.getJSONObject(1) and that throws the same exception.
Basically, I am looking to seamlessly iterate through the JSON string now. Any help?
1st off to simplify your code, use request.JSON. Then request.JSON.list[ 0 ] should be working