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
Related
I have a server set up to send messages over a local host port. I am trying to decode the serialized json messages sent by the server and get this error.
Error decoding message: kotlinx.serialization.json.internal.JsonDecodingException: Unexpected JSON token at offset 55: Expected EOF after parsing, but had instead at path: $
JSON input: .....mber":13,"Timestamp":5769784} .....
The Racer State messages are formatted in JSON as follows: { “SensorId”: “value”, “RacerBibNumber” : “value”, “Timestamp” : “value” }, where the value’s are character string representations of the field values. I have also tried changing my RacerStatus Class to take String instead of Int but to a similar error. Am I missing something here? The symbol that is missing in the error was not able to be copied over so I know it's not UTF-8.
I have also added
val inputString = bytes.toString(Charsets.UTF_8)
println("Received input: $inputString")
This gets
Received input: {"SensorId":0,"RacerBibNumber":5254,"Timestamp":3000203}
with a bunch of extraneous symbols at the end.
data class RacerStatus(
var SensorId: Int,
var RacerBibNumber: Int,
var Timestamp: Int
) {
fun encode(): ByteArray {
return Json.encodeToString(serializer(), this).toByteArray()
}
companion object {
fun decode(bytes: ByteArray): RacerStatus {
print(bytes[0])
try {
val mstream = ByteArrayInputStream(bytes)
return Json.decodeFromStream<RacerStatus>(mstream)
} catch (e: SerializationException) {
println("Error decoding message: $e")
return RacerStatus(0, 0, 0)
}
// return Json.decodeFromString(serializer(), mstream.readBytes().toString())
}
}
}
So I found an answer to my question. I added a regex to include just the json components I know my json contains.
val str = bytes.toString(Charsets.UTF_8)
val re = Regex("[^A-Za-z0-9{}:,\"\"]")
return Json.decodeFromString<RacerStatus>(re.replace(str,""))
I thought that Charsets.UTF_8 would remove the misc characters but it did not. Is there a more intiuative solution? Also is there a regex that would cover all possible values in json?
So I'm trying to split a json array with some metadata and append the metadata to each object in the array:
{
"#version":"1",
"metadata":{"key1":"value1","key2":"value2"},
"messages":[{"msg":{...}},{"msg":{...}}, ... ]
}
to
{
"type":"msg",
"data":{
"#version":"1",
"metadata":{"key1":"value1","key2":"value2"},
"msg":{...}
}
}
I have the following Camel route which almost works:
from("direct:kafkaMsg")
.routeId("rKafkaMsg")
.log(DEBUG, "JSON: ${body}")
.setHeader("#version").jsonpath("$.#version")
.setHeader("metadata").jsonpathWriteAsString("$.metadata") // not writing as string
.split().jsonpathWriteAsString("$.messages..msg")
.setHeader("msgId", jsonpath("$.msgNo"))
.setHeader("kafka.KEY", simple("${header.msgId}"))
.setBody(simple("{\"type\":\"msg\","
+ "\"data\":{\"#version\":\"${header.#version}\",\"metadata\":${header.metadata},"
+ "\"msg\":${body}}}"))
.log(DEBUG, "body: ${body}")
.log(INFO, "msgPush: writing msg to kafka - msgId: ${header.msgId}")
.setProperty(Exchange.CHARSET_NAME, constant("UTF-8"))
.to("kafka:sometopic?partitioner=" + DefaultPartitioner.class.getName())
.end();
But the Json I actually get like this is:
{
"type":"msg",
"data":{
"#version":"1",
"metadata":{key1="value1",key2="value2"}, // wrong. This looks like mapToString
"msg":{...}
}
}
When I had .split().jsonpath("$.messages..msg") before, the 'msg' object was also wrongly formatted in the output but jsonpathWriteAsString() helped in this case. I also tried jsonpath("$.metadata", String.class) but it did not help.
Does anybody know how to fix this? Thanks.
I solved this using a workaround involving a Processor:
.split().jsonpath("$.messages..msg")
.setHeader("msgId", jsonpath("$.msgNo"))
.setHeader("kafka.KEY", simple("${header.msgId}"))
// Processor instead of setBody()
.process(exchange -> {
JsonObject data = new JsonObject();
data.put("#version", exchange.getIn().getHeader("#version"));
data.put("metadata", exchange.getIn().getHeader("metadata"));
data.put("msg", exchange.getIn().getBody());
JsonObject body = new JsonObject();
body.put("type", "msg");
body.put("data", data);
exchange.getOut().setBody(body.toJson());
exchange.getOut().setHeaders(exchange.getIn().getHeaders());
})
I prefer this solution anyway because I don't have to write part of the json string myself.
I'm new to grails, the problem i'm trying to solve is pretty simple : my server should receive some json data in a request validate the data and save it to a DB.
To my best understanding I use Command Object to validate the data. the problem is that if my Command object contains a list of another class ( a secondary command object ) the parser would put in that field a jsonArray and this would ignore my secondary validation.
Parsing json ->
void handleRequest(){
def jsonObject = request.JSON
doSomething(new PrimaryCommandObject(jsonObject))
}
def doSomething(PrimaryCommandObject cmd){
if (cmd.validate()) {
respond cmd
}else{
cmd.errors.allErrors.each {
println it
}
}
}
Main Command object ->
class PrimaryCommandObject{
int val1
List<SecondaryCommandObject> collection
}
Right now in order to bypass this issue I added a setter
Setter ->
void setCollection(JSONArray jsonArray){
this.collection = []
jsonArray.each { item ->
SecondaryCommandObject obj = item as SecondaryCommandObject
if (obj.validate()) {
this.collection << obj
}else {
obj.errors.allErrors.each {
println it
}
}
}
This doesn't feel right for me, I would except a cleaner simpler way to get it done. this
can someone please help ? thanks
Please pardon me if this is a repeat question. I have been through some of the questions/answers with a similar requirement but somehow got a bit overwhelmed and confused at the same time. My requirement is:
I get a JSON string/object as a request parameter. ( eg: params.timesheetJSON )
I then have to parse/iterate through it.
Here is the JSON that my grails controller will be receiving:
{
"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"
}
Questions:
How can I retrieve the whole JSON string from the request? Does request.JSON be fine here? If so, will request.JSON.timesheetJSON yield me the actual JSON that I want as a JSONObject?
What is the best way to parse through the JSON object that I got from the request? Is it grails.converters.JSON? Or is there any other easy way of parsing through? Like some API which will return the JSON as a collection of objects by automatically taking care of parsing. Or is programatically parsing through the JSON object the only way?
Like I said, please pardon me if the question is sounding vague. Any good references JSON parsing with grails might also be helpful here.
Edit: There's a change in the way I get the JSON string now. I get the JSON string as a request paramter.
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 object1 = 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.
I have wrote some code that explains how this can be done, that you can see below, but to be clear, first the answers to your questions:
Your JSON String as you wrote above will be the contents of your POST payload to the rest controller. Grails will use its data binding mechanism to bind the incomming data to a Command object that your should prepare. It has to have fields corresponding to the parameters in your JSON String (see below). After you bind your command object to your actual domain object, you can get all the data you want, by simply operating on fields and lists
The way to parse thru the JSON object is shown in my example below. The incomming request is esentially a nested map, with can be simply accessed with a dot
Now some code that illustrates how to do it.
In your controller create a method that accepts "YourCommand" object as input parameter:
def yourRestServiceMethod (YourCommand comm){
YourClass yourClass = new YourClass()
comm.bindTo(yourClass)
// do something with yourClass
// println yourClass.timeSheetList
}
The command looks like this:
class YourCommand {
String loginName
List<Map> timesheetList = []
String overallStatus
void bindTo(YourClass yourClass){
yourClass.loginName=loginName
yourClass.overallStatus=overallStatus
timesheetList.each { sheet ->
TimeSheet timeSheet = new TimeSheet()
timeSheet.periodBegin = sheet.periodBegin
timeSheet.periodEnd = sheet.periodEnd
sheet.timesheetRows.each { row ->
TimeSheetRow timeSheetRow = new TimeSheetRow()
timeSheetRow.task = row.task
timeSheetRow.description = row.description
timeSheetRow.paycode = row.paycode
timeSheet.timesheetRows.add(timeSheetRow)
}
yourClass.timeSheetList.add(timeSheet)
}
}
}
Its "bindTo" method is the key piece of logic that understands how to get parameters from the incomming request and map it to a regular object. That object is of type "YourClass" and it looks like this:
class YourClass {
String loginName
Collection<TimeSheet> timeSheetList = []
String overallStatus
}
all other classes that are part of that class:
class TimeSheet {
String periodBegin
String periodEnd
Collection<TimeSheetRow> timesheetRows = []
}
and the last one:
class TimeSheetRow {
String task
String description
String paycode
}
Hope this example is clear enough for you and answers your question
Edit: Extending the answer according to the new requirements
Looking at your new code, I see that you probably did some typos when writting that post
def jsonArray = jsonArray.timesheetList
should be:
def jsonArray = jsonObject.timesheetList
but you obviously have it properly in your code since otherwise it would not work, then the same with that line with "println":
jsonArray1.size()
shuold be:
jsonArray.size()
and the essential fix:
def object1 = jsonArray[1]
shuold be
def object1 = jsonArray[0]
your array is of size==1, the indexing starts with 0. // Can it be that easy? ;)
Then "object1" is again a JSONObject, so you can access the fields with a "." or as a map, for example like this:
object1.get('periodEnd')
I see your example contains errors, which lead you to implement more complex JSON parsing solutions.
I rewrite your sample to the working version. (At least now for Grails 3.x)
String saveJSON // This holds the above JSON string.
def jsonObject = grails.converters.JSON.parse(saveJSON)
println jsonObject.timesheetList // output timesheetList structure
println jsonObject.timesheetList[0].timesheetRows[1] // output second element of timesheetRows array: [paycode:payCode2, task:painting, activityDescription:painting description]
I have discovered problem in JSON-Lib with deserialization of JSON string data in nested objects. Following code in Groovy demonstrates problem, you can test it in Groovy Console:
#Grab('net.sf.json-lib:json-lib:2.3:jdk15')
import net.sf.json.*
def s = '''\
{
"attributes": "'{test:1}'",
"nested": { "attributes": "'{test:1}'" }
}'''
def j = JSONSerializer.toJSON(s)
println j
assert j.attributes == "{test:1}" // OK
assert j.nested.attributes == "{test:1}" // Failed
In the example above both attributes in JSON have the same string value "{test:1}". JSONSerializer deserialize the first one into String value but second one into (maybe) JSONObject. I'm not sure because class of that object returns null.
For different string value (which doesn't look like JSON data, e.g. "blah") it works.
How can I change this behavior?
UPDATE
I have tried to quote string values based on JSON-Lib documentation here (thanks to #GemK for pointing there), but with same result. Quoting string values doesn't work in nested objects :-(