Hi Say that I have a domain class
class Book{
static hasOne=[author:Author]
long id
String name
}
class Author {
static hasMany=[books:Book]
long id
String name
}
I have a json object sent in. Can I jus do a new Book(Json) and not manually set the property?
Using the built-in Grails JSON converter makes this easier
import grails.converters.JSON
class BookController {
def save = {
def book = new Book(JSON.parse(yourJson))
book.save(flush:true)
}
}
In the code what's happening (we're parsing a JSON object and setting the properties on the Book entity and saving
use the JsonBinder:
def json = request.JSON;
def book= new Book();
JsonBinder.bindJSON(book, json);
dont forget to import these packages:
import grails.converters.JSON;
import com.ocom.grails.JsonBinder;
Related
I am new to groovy Grails and trying to understand how to work with GORM
We have SQL table with column of string type that holds JSON String representing some object
(I can't alternate db design)
I understand that in groovy Model objects represent SQL records and in general we can use marshallers to render objects to JSON
But what I need is to get, create or save Model object that have Json string column that will be rendered to an object in groovy, but can't find any information on how to do it
for example to simplify i will have following table : id(number), json(longstring)
and in JSON:
{"name":"object1", "list":[{"item":"item1", "type":"type1"},{"item":""item2", "type":"type2"},..]}
and following classes:
class MainModelClass {
Long id
MyObject o
...
}
class MyObject {
List<Item> items
...
}
class Item {
String item
String type
...
}
How can I make the Model object parse the JSON to Object structure
Thanks
You could use a simple trick with a transient property like so:
import groovy.json.*
class MainModelClass {
String originalJson
static final JsonSlurper slurper = new JsonSlurper()
MyObject getMyObject(){
slurper.parseText( originalJson ) as MyObject
}
void setMyObject( MyObject myObject ){
originalJson = JsonOutput.toJson myObject
}
static transients = [ 'myObject' ]
}
You might want to use Jackson Mapper to have finer control over marshalling.
I had a function in AWS Lambda:
def test(pj: Pojo, context: Context): java.util.List[Document]
that was not initializing the pj with the input JSON values at all.
I found another way of doing AWS Lambda in Scala like this:
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.scala.DefaultScalaModule
val scalaMapper = new ObjectMapper().registerModule(new DefaultScalaModule)
def test(input: InputStream, output: OutputStream): Unit = {
val inputPojo = scalaMapper.readValue(input, classOf[Pojo])
val answer: Seq[Document] = getTheRealAnswer(inputPojo)
val jsonStr = "{ frustration: \"I wish my answer was JSON.\" }"
output.write(jsonStr.getBytes("UTF-8"))
}
and that works, except what I really want to return as an answer is a JSON array of Documents. How should I go about that?
Edit: In my original posting, I wrote: "[the first example] was returning the answer as an error 22. Basically AWS (I think) treated the JSON conversion of the List[Document] as a filename, JSON has plenty of colons, and the error 22 came from colons in filenames not being allowed. Weird." That turned out to be an error in my invocation of the AWS Lambda Function from AWS CLI. I omitted the output filename in the command invocation, and returned JSON was interpreted by AWS CLI as a filename.
Since I wrote this message, I got things to work like this:
def jsonizeDocs(cDocument: Seq[Document]): String = {
val sb=new StringBuilder
for (doc <- cDocument) {
if (sb.nonEmpty) {
sb.append(",")
}
sb.append(doc.toJson)
}
sb.toString
}
Note! This answer is based on a light wrapper I wrote around json4s which I call JSON Extensions
Assuming you are using Scala Objects, import the io.onema.json.Extensions._
import io.onema.json.Extensions._
case class Doc(title: String, content: String)
val listOfDocs = Seq(Doc("Foo", "bar"), Doc("Bar", "Baz"), Doc("Blah", "Bax"))
val json: String = listOfDocs.asJson
println(json)
// [{"title":"Foo","content":"bar"},{"title":"Bar","content":"Baz"},{"title":"Blah","content":"Bax"}]
See the running example here
Now, since you are using a Pojo, you need to import io.onema.json.JavaExtensions._. Assuming you have the following POJO:
public class Document {
private String title;
private String content;
public String getTitle() {return title;}
public String getContent() {return content;}
public void setTitle(String title) { this.title = title;}
public void setContent(String content) {this.content = content;}
}
Use this method in your Scala code like such:
import io.onema.json.JavaExtensions._
import com.example.Document
// ...
def jsonizeDocs(cDocument: Seq[Document]): String = {
val json: String = cDocument.asJson
println(json)
json
}
In AWS Lambda (and to go the other way around) use jsonDecode and a custom object mapper to deserialize to the expected type:
import io.onema.json.JavaExtensions._
import io.onema.json.Mapper
import com.example.Document
val jsonString = """[{"title":"Foo","content":"bar"},{"title":"Bar","content":"Baz"},{"title":"Blah","content":"Bax"}]"""
val mapper: ObjectMapper = Mapper.allowUnknownPropertiesMapper
val doc: Document = jsonString.jsonDecode[Document](mapper)
I have used the method described here quite successfully in a lambda framework that is able to deserialize to AWS lambda events as well as custom types, see a simple example here.
That's it! you can use this library or one of the many JSON serializers in Java or Scala. If you know the type of your objects most libraries will enable you to serialize to JSON and back very easily.
I don't understand how can i exclude some properties when rendering an object as JSON in a controller (not a RESTController but a classic Controller).
I have this resources.groovy:
// Place your Spring DSL code here
import grails.rest.render.json.JsonRenderer
import com.appromocodes.Promocode
import com.appromocodes.ResponseStatus
import grails.rest.render.json.JsonCollectionRenderer
beans = {
responseStatusRenderer(JsonRenderer, ResponseStatus) {
excludes = ['enumType']
}
promocodeRenderer(JsonRenderer, Promocode) {
excludes = ['class', 'id', 'project']
}
}
Within my controller i tried in my action something like:
respond p as JSON
but this still gives me all the fields (also class, id and project fields).
What should i do?
The proper way to handle this is to register a custom JSON marshaller for your object. Begin by creating a new Marshaller in src/groovy/packageName/marshallers/PromocodeMarshaller.groovy with the following contents:
import packageName.Promocode
import grails.converters.JSON
class PromocodeMarshaller {
void register() {
JSON.registerObjectMarshaller(Promocode) { promocode ->
return [
id: promocode?.id,
// all the fields you'd like to return
// in your JSON object
]
}
}
}
Then, inside of your Bootstrap.groovy file, include the following:
import packageName.marshallers.PromocodeMarshaller
def promodcodeMarshaller = new PromocodeMarshaller()
promocodeMarshaller.register()
For a full description, see this article.
I am using jackson json api to map json data to java objects. All is well in case of same object attribute names with json attributes. Now i have a situation where i am getting json data attribute with -. (my-name).
In java we can't include - in variable names.
import org.codehaus.jackson.map.ObjectMapper;
private static final ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue("{my-name:\"abcd\"}", User.class);
public class User {private String my_name; /*get-set methods*/}
Is there anything i need to apply in User.class.
I don't want to change my code so much.
In your java class you can give any name as you like
Ex. private String myName;
But in the setter method just write:
#JsonProperty("my-name")
public void setMyName(String myName) {
this.myName = myName;
}
model:
class Author{
String name
static hasMany = [books: Book]
}
class Book{
String name
Author author
static belongsTo = Author
}
Then i have a controller
class MyController{
def authors{
def authors = Author.getAll()
render authors as JSON
}
The problem is that even if the association Author-Books is lazy, N+1 queries are executed to fetch eagerly the books for each Author. What does it happen and how can i disable it
You are using default JSON converter, which tries to convert all fields of your model. Thats why it is doing all those selects.
You should implement your own JSON converter for you model which would not ask DB for books. You can do it in BootStrap like this:
import grails.converters.JSON
class BootStrap {
def init = {servletContext ->
JSON.registerObjectMarshaller(Author) {
def returnArray = [:]
returnArray['name'] = it.name
return returnArray
}
}