Is it possible to send a dictionary from server to c# client? - c#

So I'm able to send simple objects like strings no problem. I'm also able to send the same dictionary to my javascript client. However, I can't seem to get my C# client to pick it up. I'm assuming this is because in the javascript client the type being sent doesn't need to be specified, but in C# it does.
Here's the client setup
connection = new HubConnection(ServerURI);
hubProxy = connection.CreateHubProxy("ControllerHub");
hubProxy.On<Dictionary<Tuple<string, string>, RequestEntry>>("ReceiveTickerStateData", overviewDictionary => receiveTickerStateData(overviewDictionary));
connection.Start().Wait();
Here's the server code that is getting triggered to send the dictionary, but the client never receives it.
public void GetTickerStateData()
{
Clients.Caller.ReceiveTickerStateData(DealTickerState.Instance.GetRequestDict);
}
Any insight or ideas are appreciated. Thanks!
------------------------Additions
Further testing has yielded that it is the fact that I'm using a tuple (string, string) as the key for the dictionary. Apparently the JSON deserializer does not know how to handle this. Gives me the error:
Could not convert string (DEFAULT, IDCO) to dictionary key type
System.Tuple`2[System.String,System.String]. Create a TypeConverter
to convert from the string to the key type object. Path '['(DEFAULT,
IDCO)']', line 1, position 19.
I got this by manually serializing and sending the string.
I tried sending a dictionary with just a string as the key and it works fine both as a serialized string and also just as is. (ie signalR automatically serializing)
So now to decide, do I create the type converter? or do I send the data using a simpler collection structure that works.

Yes, as JSON. Use JSON.NET, the included JavaScriptSerializer won't work. You can get JSON.NET from NuGet. Convert your dictionary to JSON and send it as a string. On the client-side, just deserialize it using the built-in JSON.parse() method.

You can also create an intermediary type that stores the dictionary during transfer (I use List<KeyValuePair<T1,T2>>), it's important that you validate that there aren't any duplicate keys when deserialising this from a client.
[Serializable]
public class SerializableDictionary<T1,T2>
{
public List<KeyValuePair<T1,T2>> SerializedDictionary { get; set; }
public SerializableDictionary( Dictionary<T1,T2> dictionary )
{
if( dictionary != null && dictionary.Count > 0 )
{
SerializedDictionary = dictionary.Select( item => item ).ToList();
}
else
{
throw new ArgumentNullException( "Cannot serialize a null or empty dictionary" );
}
}
public static explicit operator SerializableDictionary<T1,T2>(Dictionary<T1,T2> dictionary)
{
return new SerializableDictionary<T1,T2>(dictionary);
}
public static explicit operator Dictionary<T1,T2>(SerializableDictionary<T1,T2> dictionary)
{
if ( dictionary.SerializedDictionary == null ) return null;
return dictionary.SerializedDictionary.ToDictionary( item => item.Key, item => item.Value );
}
}
This technique wont work for every dictionary (for example, those stored in exceptions) as they can't all be easily suplemented for this service aware dictionary.

Seems that the real answer is Yes, it is possible, but you need to write a custom JSON converter. You can find more info on that here, though the answer does seem a tad incomplete.
How to properly serialize tuple as key dictionary

Related

Deserialize array that was Serialized with BinaryFormatter, without using BinaryFormatter to deserialize

BinaryFormatter is now obsolete for security reasons. Unfortunately, there is a ton of old legacy records that were serialized to array using the BinaryFormatter. Is there a way to deserialize those records in a new app WITHOUT using BinaryFormatter or are we stuck doing a massive overhaul to pull records, deserialize and reserialize in a different format?
Edit: I am trying to convert the byte[] into an object. The only other way I know how to do that is with Deseralize from System.Text.Json. But BinaryFormatter doesn't use encode, it uses a BinaryData type. System.Text.Json expects encode, utf8 not to mention it expects json.
Edit: Getting closer. Part of the issue is the format is in MS-NRBF when you use BinaryFormatter. There is a nuget that helps you read the format. (https://github.com/bbowyersmyth/BinaryFormatDataStructure) You still have to manually reflect and manage the object assignment, but at least this lets you read the MS-NRBF format.
I found this resource to be very useful: https://localcoder.org/how-to-analyse-contents-of-binary-serialization-stream
If anyone gets curious about how you can do this, here is a way. Its a rough little experiment I put together. Its not robust, just a breadcrumb. I'm not sharing this to say you should do this, since reflection is a cost. That's up to you and your requirements to decide. I'm just sharing the how. This will successfully map the MS-NRBF back into a class model. Note the recursion.
NOTE: This only works with objects. If it was a string value that was transformed originally, the library will just pull the string value out directly, so you'll need to cast it into (string) instead of (BinaryObject). You can read this to learn more about the MS-NRBF headers if you want to deeper dive into what the lib is doing: https://localcoder.org/how-to-analyse-contents-of-binary-serialization-stream
I used this lib to digest the format: https://github.com/bbowyersmyth/BinaryFormatDataStructure
private T TransformMSNRBFToObject<T>(byte[] data) where T : new()
{
T response = new T();
using (var stream = new MemoryStream(data))
{
var bObj = (BinaryObject)NRBFReader.ReadStream(stream);
GetObjectFromBinaryObject<T>(bObj, response);
}
return response;
}
private void GetObjectFromBinaryObject<T>(BinaryObject bObj, T result) where T : new()
{
foreach (var prop in result.GetType().GetProperties())
{
string key = $"<{prop.Name}>k__BackingField";
if (!bObj.ContainsKey(key))
{
throw new Exception($"Transform could not occur. Expected property {prop.Name} in data, but none was found.");
}
//If its a nested class, then the value will be another BinaryObject. Use recursion to get that next obj mapped.
if (prop.PropertyType.IsClass && prop.PropertyType != typeof(string))
{
var type1 = prop.PropertyType;
var propValue = (BinaryObject)bObj[key];
try
{
var result2 = Activator.CreateInstance(type1);
GetObjectFromBinaryObject(propValue, result2);
prop.SetValue(result, result2);
}
catch (System.MissingMethodException mme) //This happens usually when you try to map a string or system object
{
throw new Exception($"Model you are trying to convert to is missing an empty contstructor. Class: {type1}");
}
}
else //its a IConvertible field or a string
{
prop.SetValue(result, bObj[key]);
}
}
}
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter?
Can you use System.IO.BinaryReader?
I would back up the data and would do a one-time conversion of all data to a new format. Easy code and fewer chances to screw up things.

Using a Dictionary<string, Type> to use the value as type of a list

I am trying to build a solution fitting with the problem of not knowing what kind of Setting type I am dealing with.
I got a Dictionary<string, Type> (which I initially wanted to make <string, class> but that didn't work)
that I want to fill with the setting code and the type of class attached to it i.e.
{ "person_customField", typeof(CustomFieldModel) }
Why I want to do this is because I have a field in my database filled with json data that should be deserialized to a List<> but I don't know what kind of setting it is until I get the object from the database. I can use the Code field to detemine what type it is (person_CustomField should use the CustomFieldModel class, but emailSetting should use EmailSettingModel to match parameters to.
Is there a way to successfully make this statement work with?
JsonConvert.DeserializeObject<List<SettingTypes[record.SettingCode]>>(record.SettingValues).ToList<ISetting>()
Or should I go a different route
Code Sample:
public static readonly Dictionary<string, Type> SettingTypes = new Dictionary<string, Type>()
{
{ "person_CustomFields", typeof(CustomFieldModel)},
};
public static TenantSettingEdit ConvertToTenantSettingEdit(this T_TenantSetting rec)
{
var test = SettingTypes[rec.TENS_Code];
TenantSettingEdit item = new TenantSettingEdit()
{
IDToken = rec.TENS_TenantSettingID.toVirtualGuid().ToString(),
Code = rec.TENS_Code,
Settings = JsonConvert.DeserializeObject<List<SettingTypes[rec.TENS_Code]>>(rec.TENS_Setting).ToList<ITenantSetting>(),
IsActive = rec.TENS_ActiveRec,
};
return item;
}
(I have done this before with PHP but I am not sure if this is even remotely possible with C#)
Why I want to do this is because I have a field in my database filled
with json data that should be deserialized to a List<> but I don't
know what kind of setting it is until I get the object from the
database.
If you're using Json.Net for JSON serialization/deserialization you can use the TypeNameHandling property to embed Type information in the resulting JSON. That JSON can the be deserialized by Json.Net without additional information. If it is necessary to map custom values to the types instead of the automatically generated ones you can use a SerializationBinder (check out this answer).
If none of those help you, you can still fall back to reflection in the way M Kloster describes.
You cannot use a variable as the type parameter in the code, no. What you need to do is to generate the type-specific method by reflection:
var genericMethod = ((Func<string, int>)Json.DeserializeObject<int>).Method.GetGenericMethodDefinition();
var boundMethod = genericMethod.MakeGenericMethod(SettingTypes[record.SettingCode]);
var result = boundMethod.Invoke(null, rec.TENS_Setting)...

Storing JContainers (JObject, JArray, etc) in mongodb with C# driver

I have a WEB API web service that accepts an arbitrary Object that I would like to store as a property in a MongoDB document. By the time my class library gets it, it is a JObject, so simply storing it as is includes all sorts of extra cruft. I am brand new to mongo, and the only workaround I could figure out is to make the property of type BsonValue, and writing the following bit of hackery to create it:
private static BsonValue ToBsonValue(object value)
{
var jobject = value as Newtonsoft.Json.Linq.JContainer;
if (jobject != null)
{
return BsonDocument.Parse("{crazyHack: " + jobject.ToString() + "}")[0];
}
else
{
return BsonValue.Create(value);
}
}
When retrieving the value,I convert it back to an Object with BsonTypeMapper.MapToDotNetValue.
I can't imagine there isn't a more straightforward way to do this, but JContainer doesn't seem to have any methods that will generate a plain object that is not itself a JContainer. Any tips would be appreciated.

Converting a Javascript JSON.stringfy string to an object using c# JSON.NET

I am developing a windows 8 app, and i have some javascript that stores a serialized object into roaming settings, i.e:
var object = [{"id":1}, {"id":2}]
roamingSettings.values["example"] = JSON.stringify(object);
I also i have a c# part to the application (for running a background task), that needs to read that JSON, and turn it into an object so i can iterate over it. And this is where i am having some issues, i am using JSON.NET to do the work, but every thing i turn turns up with an error:
// this looks like "[{\"id\":1},{\"id\":2}]"
string exampleJSON = roaming.Values["example"].ToString();
// dont know if this is correct:
List<string> example = JsonConvert.DeserializeObject<List<string>>(exampleJSON );
That give an error of:
Error reading string. Unexpected token: StartObject. Path '[0]', line 1, position 2.
So i am at a loss of what to do, i have been working on it for last few hours, and i am quite unfamiliar with c#, so resorting to the help of stackoverflow ;D
Thanks in advance for any help :)
Andy
Json.Net has a nice method DeserializeAnonymousType. No need to declare a temporary class.
string json = "[{\"id\":1},{\"id\":2}]";
var anonymous = new []{new{id=0}};
anonymous = JsonConvert.DeserializeAnonymousType(json,anonymous);
foreach (var item in anonymous)
{
Console.WriteLine(item.id);
}
You can even use the dynamic keyword
dynamic dynObj = JsonConvert.DeserializeObject(json);
foreach (var item in dynObj)
{
Console.WriteLine(item.id);
}
You are trying to parse your JSON array into a List of strings, which doesn't work. The JSON object you provide is actually a list of objects containing an integer property called 'id'.
Perhaps try creating a class (say, MyClass) with just that property, and deserialize into List.
Your json containts a collection of objects with an id property, something like this:
class IdObject {
public int id { get; set; }
}
You could then do:
JsonConvert.DeserializeObject<List<IdObject>>(exampleJSON);
Because the IdObject class has a property id to match your json serialized value, it will be mapped back.

Conditional array/object deserialisation with Json.Net

I'm using the Json.Net library (for .NET v3.5) to handle deserialisation of responses from the EBoss CRM API. As far as I've seen the API documentation is rather spotty, and so I'm having to poke at it myself to see what kind of responses can be elicited.
I wanted to have a single wrapper class EBossApiResponse<T> that would provide properties for any error messages that the API sent back, plus a property containing the deserialised data of type T (which would be restricted to an abstract base class which acts as the underlying type of any classes I created to model the returned data).
My initial poking revealed the first problem. A valid request can return an array of objects, such as: https://ebosscrm.com/api.php/job_type.json
This will easily deserialise to a hypothetical T of List<EBossJobType>. Note that there's no error property in the results.
A malformed request to that same endpoint however returns something different: https://ebosscrm.com/api.php/job_type.json?search[foo]=1
In this case, an array is returned which contains a single object with a single property named message.
(Note, there are calls for which having a parameter named search[something] is valid, but foo is never a valid something)
There is also the possibility for an explicit error to be returned. It looks to me like the API is catching exceptions and formatting a JSON response containing the debug information: https://ebosscrm.com/api.php/candidates.json?uid=114&api_key=f34js3kj
In this case, the returned JSON is not an array, it's a single object. I'm not sure how to cater for these differing response structures. My initial thinking was to have something like:
protected bool IsNonDataResponse(string response)
{
JObject o = JObject.Parse(response);
return o.SelectToken("message") != null || o.SelectToken("error") != null;
}
I could use this method to then either deserialise directly to the right EBossApiResponse<T> type (if true, populating error messages but leaving the .Data property == null), or deserialise to the right List<EBossEntityType>, create a new EBossApiResponse<List<EBossEntityType>>, and set its .Data property.
Then I realised that o.SelectToken("message") won't work, because o would be an array, not an object. Can .SelectToken() take a pattern of the form "[0].message" to get the message property of the first item?
How about this?
JObject o = JObject.Parse(response);
JArray arr = o as JArray;
return (arr != null && (JObject)arr[0].SelectToken("message") != null)
|| o.SelectToken("error") != null;
Or am I barking completely up the wrong tree, and there's a more elegant solution to this whole thing?
You could at least replace JObject.Parse() with JToken.Parse() because JToken is the base class for both JObject and JArray. Then you can check whether the result is either a JArray or a JObject (or maybe a JValue like an integer).
var result = JToken.Parse(response);
if (result is JArray)
{
// ...
}
else if (result is JObject)
{
// ...
}
Have also a look at the documentation for JToken.

Categories

Resources