zyro23 / grails-spring-websocket

93 stars 28 forks source link

brokerMessagingTemplate.convertAndSend sends string quoted and escaped #20

Closed deusprogrammer closed 9 years ago

deusprogrammer commented 9 years ago

I don't know if this is the way this is supposed to work in STOMP, but my JSON string is being returned quoted and escaped =/

zyro23 commented 9 years ago

what you see is expected with the current release version using spring-4.0 underneath. from your description, i guess you are sending a String like brokerMessagingTemplate.convertAndSend("someString"). to get the value in your js, try using JSON.parse(message.body), just like you would do if you send an Object like brokerMessagingTemplate.convertAndSend(myPlainObjectInstance).

note: that behavior will change with grails-2.5/grails-3.0 shipping spring-4.1+. from that spring version on, plain String payloads will not be json-encoded anymore, meaning then the message.body in your js code will have the string value someString without double quoting.

hth, zyro

deusprogrammer commented 9 years ago

Thanks for the reply. Originally I was trying to pass one of my domain objects back, but that was causing an error, so I attempted to pass it back using

(object as JSON).toString()

And then the subscriber can't parse the JSON because of the added quotes.

Sent from my iPhone

On Jan 20, 2015, at 1:45 PM, zyro notifications@github.com wrote:

what you see is expected with the current release version using spring-4.0 underneath. from your description, i guess you are sending a String like brokerMessagingTemplate.convertAndSend("someString"). to get the value in your js, try using JSON.parse(message.body), just like you would do if you send an Object like brokerMessagingTemplate.convertAndSend(myPlainObjectInstance).

note: that behavior will change with grails-2.5/grails-3.0 shipping spring-4.1+. from that spring version on, plain String payloads will not be json-encoded anymore, meaning then the message.body in your js code will have the string value someString without double quoting.

hth, zyro

— Reply to this email directly or view it on GitHub.

zyro23 commented 9 years ago

does that mean you got it sorted for now like var obj = JSON.parse(JSON.parse(message.body))?

deusprogrammer commented 9 years ago

Well...no =(. The client is an iOS app.

In any case, I have tried to get it to serialize a GORM domain object in convertAndSend, but it throws an exception. It also throws and exception when I attempt to pass it a POJO. Do I need to go annotate it with Jackson JSON annotations?

The exception is as follows:

No serializer found for class org.springframework.validation.DefaultMessageCodesResolver and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: java.util.LinkedHashMap["payload"]->com.trinary.survey.ContestEntry["errors"]->grails.validation.ValidationErrors["messageCodesResolver"]).

The GORM objects have those validation entries in them...which seems to be giving the serializer problems.

zyro23 commented 9 years ago

ok very good you located the problem. so two options:

or, perhaps preferred:

brokerMessagingTemplate.convertAndSend((myDomainObject as JSON).toString())
var myJsObject = JSON.parse(JSON.parse(message.body));
alert(myJsObject.myProperty);
kastork commented 9 years ago

I'm finding the docs very hard to understand on the topic of configuring Jackson to support my domain properties. I haven't been able to get Jackson annotations to work on domain classes because Jackson isn't aware of all the grails stuff. Do you know of any good examples (in the context of a Grails app?)

For now I'm using the later option you mention, but I think the former would be better long-term.

deusprogrammer commented 9 years ago

The only problem with the second solution is that I'm not using JS as the client. I am using Android and iOS STOMP Clients.

zyro23 commented 9 years ago

@kastork for now, id suggest you stick with grails json serialization. as i said, once we move on to spring 4.1, one JSON.parse will go away because then, String payloads are sent as-is. im not yet sure what the best solution is in the end, too. wether to write spring messageconverters that are aware of grails json impl. or to use jackson to serialize the grails classes. but that one may prove tricky as you mentioned. however, each way means more infrastructure being added to the plugin and im a bit reluctant to do that right now given grails3 is peeking around the corner. once a grails3 milestone/rc working with spring-websocket plugin is out, ill revisit this topic and hopefully arrive at a solution worth some readme notes ;)

@deusprogrammer not sure i get the problem there. independent of the stomp impl, you always just get the message.body as String, no? i mean except you are doing sth. with binary streaming or so... that would mean you got to have a json lib handy anyway to get objects out of the payload? and does calling that deserialization two times (again as long we are speaking spring 4.0..) pose a problem?

deusprogrammer commented 9 years ago

In Swift, deserialization fails the first time using their built in JSON deserializer. So, no...sadly it's not an acceptable solution. Not your fault though. Still an awesome plugin.

On Tue, Feb 24, 2015 at 2:21 PM, zyro notifications@github.com wrote:

@kastork https://github.com/kastork for now, id suggest you stick with grails json serialization. as i said, once we move on to spring 4.1, one JSON.parse will go away because then, String payloads are sent as-is. im not yet sure what the best solution is in the end, too. wether to write spring messageconverters that are aware of grails json impl. or to use jackson to serialize the grails classes. but that one may prove tricky as you mentioned. however, each way means more infrastructure being added to the plugin and im a bit reluctant to do that right now given grails3 is peeking around the corner. once a grails3 milestone/rc working with spring-websocket plugin is out, ill revisit this topic and hopefully arrive at a solution worth some readme notes ;)

@deusprogrammer https://github.com/deusprogrammer not sure i get the problem there. independent of the stomp impl, you always just get the message.body as String, no? i mean except you are doing sth. with binary streaming or so... that would mean you got to have a json lib handy anyway to get objects out of the payload? and does calling that deserialization two times (again as long we are speaking spring 4.0..) pose a problem?

— Reply to this email directly or view it on GitHub https://github.com/zyro23/grails-spring-websocket/issues/20#issuecomment-75837625 .

zyro23 commented 9 years ago

2.0.0.M1 is release working together with grails-3.0.0.m2. no more double escaping for String payloads happening there. sadly no support for the grails-2.4.x line here. ok for you guys if i close this one?

deusprogrammer commented 9 years ago

Awesome! Works for me. My project is still in it's infancy anyways, so I can easily upgrade it to Grails 3.0

Thanks!

On Fri, Feb 27, 2015 at 9:27 AM, zyro notifications@github.com wrote:

2.0.0.M1 is release working together with grails-3.0.0.m2. no more double escaping for String payloads happening there. sadly no support for the grails-2.4.x line here. ok for you guys if i close this one?

— Reply to this email directly or view it on GitHub https://github.com/zyro23/grails-spring-websocket/issues/20#issuecomment-76411781 .

ZacharyKlein commented 8 years ago

I'm seeing this issue after upgrading to Grails 3.1 (regardless of whether I use spring-websocket 2.1.0 or 2.3.0). The message body is coming through as a string with escaped quote marks, which breaks the JSON.parse() call on the front-end.

In Grails 3.0.x, I get this on the client-side (using Stomp/SockJs): [{ "key": "value" }]

In 3.1.1, I get: [{ \"key\": \"value\" }]

ZacharyKlein commented 8 years ago

Okay, I got this working again, but it wasn't very straightforward. Here's the rundown:

In my 3.0.9 app, I would push JSON over web sockets like so: brokerMessagingTemplate.convertAndSend "/topic/data", json.toString() ... where json is an instance of JsonBuilder that I've used to convert my Groovy HashMap to JSON.

Then in my JS client app, I could parse the data like so: JSON.parse(message.body)

After upgrading to 3.1.1, my JSON string was being escaped as detailed above. Here's how I worked around it:

  1. Removed the toString() from my convertAndSend call: brokerMessagingTemplate.convertAndSend "/topic/data", json
    1. This fixed the string escaping, but now the parsed JSON on the JS side looked like this: ["content": [{ "key": "value" }]]. So in order to get the same data as I had in 3.0.9, my code on the front-end had to change to this: JSON.parse(message.body).content

I'm assuming something in the underlying Spring websocket implementation changed, and I'm trying to track down what exactly. This code I have works fine, but I'm a bit uneasy with it, if only because it doesn't really follow the documentation I had been following before.

zyro23 commented 8 years ago

please note that the cause for the behavior is an issue in spring-boot that got fixed in the meantime (spring-boot-1.3.3) but grails is currently still using spring-boot-1.3.2. so keep in mind that with the next grails upgrade, your JSON.parse(message.body), may become unnecessary again. for the root cause and other (more general) types of fixes please see: