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.
Related
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.
Most of the time we don't, by default, have defined classes in our static language of choice, which presents white a quandary over how to deserialize the e.g. JSON returned from a call to a public and open wen api, dedicated to no single language, except the "language" of e.g. HTML and JSON.
In .NET, these days, nearly all api queries are done with HttpClient, something like the Google Books API ISBN query below:
public class GoogleBooksClient
{
private const string IsbnUrl = "books/v1/volumes?q=isbn:{0}";
private static HttpClient _client = new HttpClient();
...
public async Task<object> GetDetailsByIsbn(string isbn)
{
var json = await _client.GetStringAsync(string.Format(IsbnUrl, isbn));
dynamic objX = JsonConvert.DeserializeObject(json);
return objX;
}
}
The biggest problem here is that whether objX is declared var', object, or dynamic, it is always a reference to a big, ugly JObject instance when DeserializeObject is called without a known type. In this case, the JSON object returned is incredibly complex, and there be dragons waiting for those who endeavour to write a C# class that the said JSON can be parsed into.
This is an ideal, and intended, opportunity for the use of a C# dynamic object, where as the JSON is parsed, properties (and rarely functions for API responses) can be recursively added to the said dynamic object.
Instead, NewtonSoft.JSON tightly binds the data held by the JSON with the heavy red-tape ligature of a quite opaque Jobject data structure. I'm surprised and disappointed that having come so far, NewtonSoft cannot simply extract a pure dynamic object, without all the obfuscating bureaucracy of their JObject maze.
Is there no other way to simply parse the JSON into a C# dynamic object, just like it would be parsed into an explicitly dynamic JavaScript object in other scenarios?
ADDED: By "obfuscating bureaucracy of their JObject maze", I mean the following code:
var json = JsonConvert.SerializeObject(new Person {Id = 196912135012878, Age = 47, Name = "Brady"});
var jObj = JsonConvert.DeserializeObject(json);
produces a JObject instance that my debugger shows as:
This looks, to me, much more like something that should be used internally during parsing of the JSON, and not an end product of that parsing, which should be a pure representation of only the properties parsed from that JSON. That would ideally be a C# dynamic object with no properties for use by any parser.
Rather than a JObject, you can deserialize to an ExpandoObject, which is supported by Json.NET's built-in converter ExpandoObjectConverter. If an array, you can deserialize to a List<ExpandoObject> then select to a List<dynamic>. If a primitive value, you could return JValue.Value.
For instance, if you know in advance your JSON represents an object, just do:
dynamic dyn = JsonConvert.DeserializeObject<ExpandoObject>(json);
If you don't know in advance what the root JSON container might be, you can load it as a JToken and use the following extension method:
public static class JsonExtensions
{
public static dynamic ToDynamic(this JToken token)
{
if (token == null)
return null;
else if (token is JObject)
return token.ToObject<ExpandoObject>();
else if (token is JArray)
return token.ToObject<List<ExpandoObject>>().Cast<dynamic>().ToList();
else if (token is JValue)
return ((JValue)token).Value;
else
// JConstructor, JRaw
throw new JsonSerializationException(string.Format("Token type not implemented: {0}", token));
}
}
Then do:
dynamic dyn = JsonConvert.DeserializeObject<JToken>(json).ToDynamic();
Sample fiddle.
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
I have a json string that has multiple class types. I want to be able to parse the json file and also cast the objects dynamically.
Example:
object jsonInstanceOfObject = LitJson.JsonMapper.ToObject<Type.GetType(classTypeString)>(jsonString);
Is this even possible?
First, determine the object structure from the json string. And you can do that either yourself by looking at it or you can also use the tool json2csharp.com (also mentioned above by L.B)-its really a handy too. It saves you time.
Once you know what the class structure corresponding to the json string is going to be, lets call it T for now, the following will do it.
private async Task<T> ParseJsonToObjectAsync(string jsonValue)
{
var obj = await JsonConvert.DeserializeObjectAsync<T>(jsonValue);
return obj;
}
If you are not using async,you can use this:
private T ParseJsonToObject(string jsonValue)
{
var obj = JsonConvert.DeserializeObject<T>(jsonValue);
return obj;
}
And that json serializer/deserializer is part of Newtonsoft.Json
Hope it helps.
Happy coding :)
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.