Oyatel / CometD.NET

CometD.NET is a C# client library for the Bayeux protocol
43 stars 34 forks source link

Deserializing the IMessage #14

Closed MrGibbage closed 12 years ago

MrGibbage commented 12 years ago

I am using System.Web.Script.Serialization.JavaScriptSerialize to serialize and deserialize my messages. I am having trouble deserializing the messages. Here is a sample message:

{ "id":"3072", "data": { "Application": "{ \"Name\":\"growlnotify\", \"Icon\": { \"Url\":null, \"Data\":null, \"IsSet\":false, \"IsRawData\":false, \"IsUrl\":false }, \"MachineName\":\"ANARKI\", \"SoftwareName\":\"GrowlConnector\", \"SoftwareVersion\":\"1.0.0.0\", \"PlatformName\": \"Microsoft Windows NT 6.1.7601 Service Pack 1\", \"PlatformVersion\":\"6.1.7601.65536\", \"CustomTextAttributes\":{}, \"CustomBinaryAttributes\":{} }", "Encrypted":"true", "RequestInfo":" { \"ReceivedFrom\":\"127.0.0.1\", \"ReceivedBy\":\"ANARKI (86c9ad7e-6894-471f-b8cd-141601c3afa9)\", \"ReceivedWith\":\"Growl/Win\", \"TimeReceived\":\"\/Date(1327489554253)\/\", \"RequestID\":\"4ec3df33-ec08-4759-9ec3-da3e1c4824d8\", \"PreviousReceivedHeaders\":[], \"HandlingInfo\": [ \"Notification Origin: 127.0.0.1 [LOCAL MACHINE]\", \"Forwarding to http://pelorus.org:8080/cometd (skip@pelorus.org)\" ] }", "NotificationTypes":" [ { \"Name\":\"General Notification\", \"DisplayName\":\"General Notification\", \"Icon\": { \"Url\":null, \"Data\":null, \"IsSet\":false, \"IsRawData\":false, \"IsUrl\":false }, \"Enabled\":true, \"MachineName\":\"ANARKI\", \"SoftwareName\":\"Growl/Win\", \"SoftwareVersion\":\"2.0.8.1\", \"PlatformName\":\"Microsoft Windows NT 6.1.7601 Service Pack 1\", \"PlatformVersion\":\"6.1.7601.65536\", \"CustomTextAttributes\":{}, \"CustomBinaryAttributes\":{} } ]", "MessageType":"Registration" }, "channel":"/chat/skip@pelorus.org" }

What methods are people here using to deserialize their JSON objects? Anyone have any sample code I could look at?

grEvenX commented 12 years ago

There's no wonder you are having issues deserializing it, the JSON you pasted is invalid.

You can't escape the quotes like that. Your error is probobly on the side where you serialize your objects. The CometD library already serialize/deserialize JSON so you shouldn't have to...

MrGibbage commented 12 years ago

Yeah, that was definitely wrong. I had been trying several different things and that was just the latest try.

Here's how I serialize:

System.Web.Script.Serialization.JavaScriptSerializer oSerializer = new System.Web.Script.Serialization.JavaScriptSerializer(); String serNotificationTypes = oSerializer.Serialize(notificationTypes); // a List of instances of the NotificationTypes class String serApplication = oSerializer.Serialize(application); // an instance of the Application class String serRequestInfo = oSerializer.Serialize(requestInfo); // an instnace of the requestInfo class Dictionary<String, Object> d = new Dictionary<String, Object>(); d.Add("Encrypted", "true"); d.Add("MessageType", "Registration"); d.Add("NotificationTypes", serNotificationTypes); d.Add("Application", serApplication); d.Add("RequestInfo", serRequestInfo); my_channel.publish(d);

Then, to deserialize, I have been trying different things.

public static void PrepareGrowlRegistration(IMessage message) { dataDict = message.DataAsDictionary; System.Web.Script.Serialization.JavaScriptSerializer oSerializer = new System.Web.Script.Serialization.JavaScriptSerializer(); MyGrowlApplication mga = new MyGrowlApplication(); try { String app = dataDict["Application"].ToString(); System.IO.File.WriteAllText(@"C:\temp\WriteText.txt", app); mga = oSerializer.Deserialize(app); // throws an error: "No parameterless constructor defined for type of 'System.String'" } catch (Exception e) { MessageBox.Show(e.Message + "\r" + e.StackTrace); }

The received JSON for "Application" looks this:

{ "Name":"growlnotify", "Icon": { "Url":null, "Data":null, "IsSet":false, "IsRawData":false, "IsUrl":false }, "MachineName":"ANARKI", "SoftwareName":"GrowlConnector", "SoftwareVersion":"1.0.0.0", "PlatformName":"Microsoft Windows NT 6.1.7601 Service Pack 1", "PlatformVersion":"6.1.7601.65536", "CustomTextAttributes":{}, "CustomBinaryAttributes":{} }

So it looks like I am getting closer, but I still have not cracked the code. Right now I am stuck on the lack of a paramaterless constructor for Strings

Any ideas?

grEvenX commented 12 years ago

Why are you trying to deserialize it? It is already deserialized into a Dictionary by the library?

MrGibbage commented 12 years ago

LOL, because I don't know what I am doing :)

So, if I have that dictionary, with that JSON as I put in my last post for the "Application", then how will I get access to the individual elements: "Name", "Icon", "MachineName", etc.

And how are Icon, CustomTextAtrributes and CustomBinaryAttributes handled, since they are "JSON within a JSON"??? In other words, how will I access the Icon.Url?

If you haven't figured it out yet, I am kinda new at this, so go easy on me :) But, seriously, thanks for your help :)

grEvenX commented 12 years ago

You shouldn't think of the data as "JSON" at all since that's only the way it's transported. The way to work with this data is more of a generic C# question than about using cometd, but here's an probably ok reference to understand how to work with dictionaries in C#:

http://www.dotnetperls.com/dictionary

MrGibbage commented 12 years ago

Sorry, that isn't where I am having trouble. I get the dictionary concept (quite well, actually). This is just a dictionary with String keys and objects as the value. The object is really just a String that looks a lot like JSON. You suggest to not think of it as JSON, in which case, I don't know how I would access the individual elements that are stored in that string. In this case, the Application key of the dictionary contains

{ "Name":"growlnotify", "Icon": { "Url":null, "Data":null, "IsSet":false, "IsRawData":false, "IsUrl":false }, "MachineName":"ANARKI", "SoftwareName":"GrowlConnector", "SoftwareVersion":"1.0.0.0", "PlatformName":"Microsoft Windows NT 6.1.7601 Service Pack 1", "PlatformVersion":"6.1.7601.65536", "CustomTextAttributes":{}, "CustomBinaryAttributes":{} }

I don't know how I would access any of the named elements in that string, such as the Name, Icon, and MachineName.

I totally get that this is no longer a CometD.Net question, but I was hoping that since it is directly related to the use of CometD, that maybe I could still get some help here.

By the way, I have read up on JSON serializers and deserializers and they are usually implemented as

//serialize MyClass c = new MyClass(); System.Web.Script.Serialization.JavaScriptSerializer oSerializer = new System.Web.Script.Serialization.JavaScriptSerializer(); string sJSON = oSerializer.Serialize(c);

//deserialize MyClass mc = oSerializer.Deserialize(sJSON);

So, I really thought I would be able to do the same here. If I can't, that's fine. I'm just trying to learn how this all works. In my case, I add the sJSON to the data dictionary along with some other serialized data. Then, when I retrieve it, I would just deserialize it and be good to go with a new instance of that class. Am I missing something?

grEvenX commented 12 years ago

Are you developing both the client and the server in your case? What is the data-type on the value of dataDict["Application"] ? Isn't that itself also a Dictionary, and as such you would access it as dataDict["Application"]["Name"] ?

MrGibbage commented 12 years ago

dataDict is declared as IDictionary<String, object> dataDict;

I've been casting them to string and trying to feed them into the deserializer, but that hasn't been working. I don't think there is any way to access Name like that. In this case, dataDict["Application"] is an object, in this case, a string equal to

{ "Name":"growlnotify", "Icon": { "Url":null, "Data":null, "IsSet":false, "IsRawData":false, "IsUrl":false }, "MachineName":"ANARKI", "SoftwareName":"GrowlConnector", "SoftwareVersion":"1.0.0.0", "PlatformName":"Microsoft Windows NT 6.1.7601 Service Pack 1", "PlatformVersion":"6.1.7601.65536", "CustomTextAttributes":{}, "CustomBinaryAttributes":{} }

Yeah, it looks like a dictionary (with a lower-case "d"), but it is not a Dictionary<T,T> class.

To confirm, I just tried it and I get a compiler error:

public static void PrepareGrowlRegistration(Cometd.Bayeux.IMessage message) { IDictionary<String, object> dataDict; dataDict = message.DataAsDictionary; String name = dataDict["Application"]["Name"]; // error: Cannot apply indexing with [] to an expression of type 'object'

Anyway, to answer your first question, yes, I am writing both the server and client. I have the bayeux cometd server running on my web server. I have one sender client that sends notifications to the server and I have a different client that long polls and waits for notifications to come in. I believe the sender side is working OK, as I am getting valid JSON incoming into the IMessage, which is further converted to a dictionary through the message.dataAsDictionary. My problem is (what I think is known as) deserialization. Even if I can't directly deserialize directly back into the original class type, I will be happy with directly accessing the elements within the JSON message and using those values to construct a new instance of that class. The problem is, I don't know how to do that either.

I guess at its simplest, my question is, what can I do with the IMessage once it is received? Especially if the data payload contains serialized instances of some classes. I guess that since this IMessage is a Cometd.Bayeux namespace, I should ask on the CometD forums?

grEvenX commented 12 years ago

This isn't an topic for the Cometd guys, this is about serialization and C#. I suggest you take a look at our own code example on how we interpret messages received from the Cometd channels here:

https://github.com/Oyatel/oyatel-api-examples/tree/master/csharp/Oyatel.Connect.Tutorial

See first StreamingEvents.cs, line 107-118, then take a look at DemoForm.cs, line 94 - 137.

MrGibbage commented 12 years ago

That helps. A little. What I think is going wrong is I am using the data dictionary to store serialized representations of objects when it was really intended to hold simpler key-value pairs. I just need help with Serialization/deserialization. You are right, this is not a question for the Cometd guys, but maybe it would be better on StackOverflow as a general question about serialization (as you suggested).

Do you have any OnCallEvent(IDictionary) code samples?

grEvenX commented 12 years ago

The code sample is already there if you took a look at the codelines I mentioned in the last comment...