i making restful twitter/facebook clone using grails , angularjs, it's standard user can post, user can posts , user can follow other users.
i using json object marshallers attributes of hasmany or belongsto of domain class rendered in json.
i have inserted necessary relationships between user , post domain feature , when implement them in bootstrap.groovy , if send get request api/posts/ works fine problem when implement them using button. button sends put request api/posts/:id , goes update() method of postcontroller. being inserted database if send again get request api/posts error
....error | 2015-09-26 00:54:35,986 [http-bio-8090-exec-9] error errors.grailsexceptionresolver - jsonexception occurred when processing request: [get] /restsocnet/api/posts misplaced endarray.. stacktrace follows: message: misplaced endarray. line | method ->> 202 | value in grails.converters.json - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | 162 | convertanother in '' | 202 | value . . . . . . in '' | 162 | convertanother in '' | 202 | value . . . . . . in '' | 162 | convertanother in '' | 202 | value . . . . . . in '' | 162 | convertanother in '' | 202 | value . . . . . . in '' | 162 | convertanother in '' | 202 | value . . . . . . in '' | 162 | convertanother in '' | 202 | value . . . . . . in '' | 162 | convertanother in '' | 202 | value . . . . . . in '' | 162 | convertanother in '' | 202 | value . . . . . . in '' | 134 | render in '' | 150 | render . . . . . . in '' | 19 | index in com.patrickjuen.restsocnet.postcontroller | 198 | dofilter . . . . . in grails.plugin.cache.web.filter.pagefragmentcachingfilter | 63 | dofilter in grails.plugin.cache.web.filter.abstractfilter | 118 | processfilterchain in grails.plugin.springsecurity.rest.resttokenvalidationfilter | 84 | dofilter in '' | 53 | dofilter . . . . . in grails.plugin.springsecurity.web.filter.grailsanonymousauthenticationfilter | 143 | dofilter in grails.plugin.springsecurity.rest.restauthenticationfilter | 62 | dofilter . . . . . in grails.plugin.springsecurity.web.authentication.logout.mutablelogoutfilter | 82 | dofilter in com.brandseye.cors.corsfilter | 1142 | runworker . . . . in java.util.concurrent.threadpoolexecutor | 617 | run in java.util.concurrent.threadpoolexecutor$worker ^ 745 | run . . . . . . . in java.lang.thread these codes
user.groovy
package com.patrickjuen.restsocnet class user implements serializable { private static final long serialversionuid = 1 transient springsecurityservice string username string password boolean enabled = true boolean accountexpired boolean accountlocked boolean passwordexpired static hasmany = [posts: post, likedpost: post] static mappedby = [posts: "user"] user(string username, string password) { this() this.username = username this.password = password } @override int hashcode() { username?.hashcode() ?: 0 } @override boolean equals(other) { is(other) || (other instanceof user && other.username == username) } @override string tostring() { username } set<role> getauthorities() { userrole.findallbyuser(this)*.role } def beforeinsert() { encodepassword() } def beforeupdate() { if (isdirty('password')) { encodepassword() } } protected void encodepassword() { password = springsecurityservice?.passwordencoder ? springsecurityservice.encodepassword(password) : password } static transients = ['springsecurityservice'] static constraints = { username blank: false, unique: true password blank: false likedpost nullable: true } static mapping = { password column: '`password`' posts lazy: false, sort: 'datecreated', order: 'desc' likedpost lazy: false } } package com.patrickjuen.restsocnet import grails.converters.json //import grails.plugin.springsecurity.annotation.secured import org.springframework.security.access.annotation.secured @secured(['isfullyauthenticated()']) class postcontroller { static allowedmethods = [save: "post", update: "put", delete: "delete"] def springsecurityservice def index() { render post.list(sort: "datecreated", order: "desc") json } def save(){ def newpost = new post(request.json) if(!newpost.haserrors()){ def currentuser = user.get(springsecurityservice.principal.id) println currentuser newpost.user = currentuser newpost.save(failonerror: true) // currentuser.addtoposts(newpost) render (['success': true] json) } } def show(){ def post = post.get(params.id) render post json } def update(){ def post = post.findbyid(params.id) if(!post.haserrors()){ def currentuser = user.get(springsecurityservice.principal.id) post.addtolikers(currentuser) post.save(flush: true) render(['success': true] json) } } } post.groovy
package com.patrickjuen.restsocnet class post { string content date datecreated user user static belongsto = user static hasmany = [likers: user] // static mappedby = [likers: "likedpost"] static constraints = { likers nullable: true } static mapping = { likers lazy: false } } bootstrap.groovy
import com.patrickjuen.restsocnet.post import com.patrickjuen.restsocnet.role import com.patrickjuen.restsocnet.user import com.patrickjuen.restsocnet.userrole import grails.converters.json class bootstrap { def init = { servletcontext -> json.registerobjectmarshaller(user) { def returnarray = [:] returnarray['id'] = it.id returnarray['username'] = it.username returnarray['posts'] = it.posts return returnarray } json.registerobjectmarshaller(post) { def returnarray = [:] returnarray['id'] = it.id returnarray['content'] = it.content returnarray['datecreated'] = it.datecreated returnarray['user'] = it.user returnarray['likers'] = it.likers return returnarray } def role = new role(authority: "role_user") def user1 = new user(username: "user1", password: "password") def user2 = new user(username: "user2", password: "password") role.save() user1.save() user2.save() def post1 = new post(content: "new post number 1") def post2 = new post(content: "new post number 2") def post3 = new post(content: "one more ") def post4 = new post(content: "i user2 guys hehehe") post1.save() post2.save() post3.save() post4.save() user1.addtoposts(post1) user1.addtoposts(post2) user1.addtoposts(post3) user2.addtoposts(post4) post2.addtolikers(user2) post2.addtolikers(user1) post1.addtolikers(user1) userrole.create(user1, role, true) userrole.create(user2, role, true) } def destroy = { } } postcontroller.groovy
package com.patrickjuen.restsocnet import grails.converters.json //import grails.plugin.springsecurity.annotation.secured import org.springframework.security.access.annotation.secured @secured(['isfullyauthenticated()']) class postcontroller { static allowedmethods = [save: "post", update: "put", delete: "delete"] def springsecurityservice def index() { render post.list(sort: "datecreated", order: "desc") json } def save(){ def newpost = new post(request.json) if(!newpost.haserrors()){ def currentuser = user.get(springsecurityservice.principal.id) println currentuser newpost.user = currentuser newpost.save(failonerror: true) // currentuser.addtoposts(newpost) render (['success': true] json) } } def show(){ def post = post.get(params.id) render post json } def update(){ def post = post.findbyid(params.id) if(!post.haserrors()){ def currentuser = user.get(springsecurityservice.principal.id) post.addtolikers(currentuser) post.save(flush: true) render(['success': true] json) } } } i tried removing json object marshallers , doesn't give out error anymore won't able access attributes of hasmany. guess main problem json object marshallers. implementing them wrongly? or there alternative using json object marshallers? yet again works fine implement them in bootstrap.groovy also, tried changing post.save(flush:true) post.save() doesn't give out error not being saved in database.
just in case interested in json being rendered. get request api/posts/
without json object marshallers
[ { "class": "com.patrickjuen.restsocnet.post", "id": 4, "content": "i user2 guys hehehe", "datecreated": "2015-09-25t17:39:10z", "likers": [], "user": { "class": "com.patrickjuen.restsocnet.user", "id": 2 } }, { "class": "com.patrickjuen.restsocnet.post", "id": 3, "content": "one more", "datecreated": "2015-09-25t17:39:10z", "likers": [], "user": { "class": "com.patrickjuen.restsocnet.user", "id": 1 } }, { "class": "com.patrickjuen.restsocnet.post", "id": 2, "content": "new post number 2", "datecreated": "2015-09-25t17:39:10z", "likers": [ { "class": "com.patrickjuen.restsocnet.user", "id": 1 }, { "class": "com.patrickjuen.restsocnet.user", "id": 2 } ], "user": { "class": "com.patrickjuen.restsocnet.user", "id": 1 } }, { "class": "com.patrickjuen.restsocnet.post", "id": 1, "content": "new post number 1", "datecreated": "2015-09-25t17:39:10z", "likers": [ { "class": "com.patrickjuen.restsocnet.user", "id": 1 } ], "user": { "class": "com.patrickjuen.restsocnet.user", "id": 1 } } ] with json object marshallers
[ { "id": 4, "content": "i user2 guys hehehe", "datecreated": "2015-09-25t17:50:58z", "user": { "id": 2, "username": "user2", "posts": [ { "_ref": "../../../..", "class": "com.patrickjuen.restsocnet.post" } ] }, "likers": [] }, { "id": 3, "content": "one more", "datecreated": "2015-09-25t17:50:58z", "user": { "id": 1, "username": "user1", "posts": [ { "_ref": "../../../..", "class": "com.patrickjuen.restsocnet.post" }, { "id": 2, "content": "new post number 2", "datecreated": "2015-09-25t17:50:58z", "user": { "_ref": "../../../..", "class": "com.patrickjuen.restsocnet.user" }, "likers": [ { "_ref": "../../../../..", "class": "com.patrickjuen.restsocnet.user_$$_javassist_5" }, { "id": 2, "username": "user2", "posts": [ { "id": 4, "content": "i user2 guys hehehe", "datecreated": "2015-09-25t17:50:58z", "user": { "_ref": "../../../..", "class": "com.patrickjuen.restsocnet.user" }, "likers": [] } ] } ] }, { "id": 1, "content": "new post number 1", "datecreated": "2015-09-25t17:50:58z", "user": { "_ref": "../../../..", "class": "com.patrickjuen.restsocnet.user" }, "likers": [ { "_ref": "../../../../..", "class": "com.patrickjuen.restsocnet.user_$$_javassist_5" } ] } ] }, "likers": [] }, { "id": 2, "content": "new post number 2", "datecreated": "2015-09-25t17:50:58z", "user": { "id": 1, "username": "user1", "posts": [ { "id": 3, "content": "one more", "datecreated": "2015-09-25t17:50:58z", "user": { "_ref": "../../../..", "class": "com.patrickjuen.restsocnet.user" }, "likers": [] }, { "_ref": "../../../..", "class": "com.patrickjuen.restsocnet.post" }, { "id": 1, "content": "new post number 1", "datecreated": "2015-09-25t17:50:58z", "user": { "_ref": "../../../..", "class": "com.patrickjuen.restsocnet.user" }, "likers": [ { "_ref": "../../../../..", "class": "com.patrickjuen.restsocnet.user_$$_javassist_5" } ] } ] }, "likers": [ { "id": 1, "username": "user1", "posts": [ { "id": 3, "content": "one more", "datecreated": "2015-09-25t17:50:58z", "user": { "_ref": "../../../..", "class": "com.patrickjuen.restsocnet.user" }, "likers": [] }, { "_ref": "../../../../..", "class": "com.patrickjuen.restsocnet.post" }, { "id": 1, "content": "new post number 1", "datecreated": "2015-09-25t17:50:58z", "user": { "_ref": "../../../..", "class": "com.patrickjuen.restsocnet.user" }, "likers": [ { "_ref": "../../../../..", "class": "com.patrickjuen.restsocnet.user_$$_javassist_5" } ] } ] }, { "id": 2, "username": "user2", "posts": [ { "id": 4, "content": "i user2 guys hehehe", "datecreated": "2015-09-25t17:50:58z", "user": { "_ref": "../../../..", "class": "com.patrickjuen.restsocnet.user" }, "likers": [] } ] } ] }, { "id": 1, "content": "new post number 1", "datecreated": "2015-09-25t17:50:58z", "user": { "id": 1, "username": "user1", "posts": [ { "id": 3, "content": "one more", "datecreated": "2015-09-25t17:50:58z", "user": { "_ref": "../../../..", "class": "com.patrickjuen.restsocnet.user" }, "likers": [] }, { "id": 2, "content": "new post number 2", "datecreated": "2015-09-25t17:50:58z", "user": { "_ref": "../../../..", "class": "com.patrickjuen.restsocnet.user" }, "likers": [ { "_ref": "../../../../..", "class": "com.patrickjuen.restsocnet.user_$$_javassist_5" }, { "id": 2, "username": "user2", "posts": [ { "id": 4, "content": "i user2 guys hehehe", "datecreated": "2015-09-25t17:50:58z", "user": { "_ref": "../../../..", "class": "com.patrickjuen.restsocnet.user" }, "likers": [] } ] } ] }, { "_ref": "../../../..", "class": "com.patrickjuen.restsocnet.post" } ] }, "likers": [ { "id": 1, "username": "user1", "posts": [ { "id": 3, "content": "one more", "datecreated": "2015-09-25t17:50:58z", "user": { "_ref": "../../../..", "class": "com.patrickjuen.restsocnet.user" }, "likers": [] }, { "id": 2, "content": "new post number 2", "datecreated": "2015-09-25t17:50:58z", "user": { "_ref": "../../../..", "class": "com.patrickjuen.restsocnet.user" }, "likers": [ { "_ref": "../../../../..", "class": "com.patrickjuen.restsocnet.user_$$_javassist_5" }, { "id": 2, "username": "user2", "posts": [ { "id": 4, "content": "i user2 guys hehehe", "datecreated": "2015-09-25t17:50:58z", "user": { "_ref": "../../../..", "class": "com.patrickjuen.restsocnet.user" }, "likers": [] } ] } ] }, { "_ref": "../../../../..", "class": "com.patrickjuen.restsocnet.post" } ] } ] } ]
i found solution. code needs inserted in config.groovy
grails.converters.json.circular.reference.behaviour = "insert_null"
i saw in 1 of comments in article http://manbuildswebsite.com/2010/02/15/rendering-json-in-grails-part-3-customise-your-json-with-object-marshallers/
Comments
Post a Comment