i asked myself if there is a generic approach where i can parse requests in a generic way or add fields to a JSON response with every response sent.
I would like to receive and parse something like:
{
transactionId:456, // every response contains this
statuscode:1,
content:{ // only content is changing
{
class:"org.something.test",
id:123,
name:"test",
referenceIdToOtherClass:345
}
}
}
The contents of "content" should be converted e.g. to an instance of the given class.
The response should work also in such generic way (changing content, some fields always).
Would you reccomend a JSON marshaller, a Filter or something different?
Thank you
You can convert the json to a map with JSON.parse
import grails.converters.*
import org.codehaus.groovy.grails.web.json.*; // package containing JSONObject, JSONArray,...
def o = JSON.parse("{ foo: 'bar' }"); // Parse a JSON String
assert o instanceof JSONObject // In this case, JSON.parse returns a JSONObject instance
assert o instanceof Map // which implements the Map interface
assert o.foo == 'bar' // access a property
and then you can pass the map to your constructor to create a new instance
from http://grails.org/Converters+Reference
Related
While implementing a simple credential store based on JSON files I ran into a problem where I need to do a seemingly unnecessary roundtrip back to JSON for Dart's oauth2.Credentials.fromJson() constructor to work.
Background: I am following the oauth2 package's Authorization Code Grant example but with the difference that instead of a single Credentials object, I want to save a list of Credentials.
Here is a stripped-down version of my credential store:
import 'dart:convert';
import 'dart:io';
import 'package:oauth2/oauth2.dart';
class CredentialStore {
List<Credentials> get credentials {
// credentials.json would contain a JSON array of objects created with oauth2.Credentials.toJson()
final file = File('credentials.json');
final jsonString = file.readAsStringSync();
final List cred = jsonDecode(jsonString);
return List<Credentials>.from(cred.map((e) => Credentials.fromJson(e)));
}
}
The last line is adapted from this answer and is accepted by the compiler but fails like this on runtime:
Unhandled exception:
type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'String'
The code, however, runs successfully, if I take a round-trip back to JSON when mapping, like so:
return List<Credentials>.from(cred.map((e) => Credentials.fromJson(jsonEncode(e))));
This seems unnecessary and looks bad to me. Is this essentially a problem with the Credentials.fromJson() implementation in that it cannot accept maps already parsed from JSON or is it possible to rewrite my get credentials implementation in a way that avoids encoding back to JSON?
The Credentials class exposes the Credentials() constructor, which is what fromJson also uses.
If you take a look at the implementation of the fromJson factory, it does some basic validation on the JSON object (i.e. checks that it's valid JSON and the required fields are present) and passes the parsed data to Credentials().
Since your data comes from Credentials.toJson() that you control yourself, it's probably safe to forgo the validation and use the constructor directly:
import 'dart:convert';
import 'dart:io';
import 'package:oauth2/oauth2.dart';
class CredentialStore {
List<Credentials> get credentials {
final file = File('credentials.json');
final jsonString = file.readAsStringSync();
final List cred = jsonDecode(jsonString);
return List<Credentials>.from(cred.map((parsed) {
var tokenEndpoint = Uri.parse(parsed['tokenEndpoint']);
var expiration = DateTime.fromMillisecondsSinceEpoch(parsed['expiration']);
return Credentials(
parsed['accessToken'],
refreshToken: parsed['refreshToken'],
idToken: parsed['idToken'],
tokenEndpoint: tokenEndpoint,
scopes: (parsed['scopes'] as List).map((scope) => scope as String),
expiration: expiration
);
}));
}
}
Regarding your compiler error: in the answer you've linked, the fromJson method there operates on the result of a JSON decoded Map, whereas the Credentials.fromJson() factory operates on a String and as such does not expect the value passed to it to be already decoded.
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?
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 a method in my main controller that return a string that I want to render as JSON.
So I am importing "import grails.converters.JSON" and calling
myMethod() as JSON
, and it works fine. But when I need to get some details of the json response in my integration test.
So in my integration test I have:
void testfoo() {
def bar = controller.myMethod();
def bar.name; //fails
JSON.parse(bar.toString()).name; // doesn't fail
....
..
}
any idea why I need to convert it to a string and then again to a JSON, since it already a JSON?
The value you get back from your method is a grails.converters.JSON, which is not a directly accessible JSON tree as such, but simply an object that knows how to serialize itself as JSON when required. If you want direct access to the JSON tree structure then you need to tell the grails.converters.JSON object to serialize itself and then pass that JSON to JSON.parse to turn it into a JSONElement (or one of its subclasses, in this case presumably a JSONObject).
I want every JSON response to post-request to contain a field success. What's the best way to add this field there?
I use code like this to generate JSON responses:
try {
def entity = myService.saveEntity(arg1,arg2)
render entity as JSON //I want to add artificial field 'success = "yes"' here
} catch (ValidationException e) {
render parseErrors(e.errors) as JSON //field 'success = "no"' here
}
I just struggled with this exact issue this week. I wanted to send back a domain class as JSON but at the same time add an 'errorMessage' property that would potentially contain additional information.
Turns out that when using as JSON in grails it sends back a converter object, but its possible to turn that converter instance into a jsonObject using JSON.parse() which we can easily add new values to.
def jsonObject = JSON.parse((entity AS JSON).toString())
jsonObject.put("success", "yes")
render jsonObject as JSON
I think there are a couple of different approaches but this ended up being the easiest for me since I already have custom converters for most of my domain classes and I didn't want to add any other transient properties to my domain object.
Could you return a map containing the success field, and the object wrapped inside a separate variable:
try {
def entity = myService.saveEntity(arg1,arg2)
render [ success:'yes', val:entity ] as JSON
} catch (ValidationException e) {
render [ success:'no', val:parseErrors(e.errors) ] as JSON
}
Not tested it mind...
You can register your own JSON marshaller (at BootStrap.groovy, for example), like:
JSON.registerObjectMarshaller(MyEntity) { MyEntity it ->
return [
someField : it.someField, // you should specify fields for output, or put all '.properties'
success : true // as I understand you always have 'true' for real entity
]
}
where MyEntity is your class you want to use