MongoDB - how to deserialize collection to List<Dictionary<string, object>> - c#

I'm switching my application from working with SQL Server to MongoDB.
One of the main reason for doing it is the possibility to use structure-less collections.
In my "SQL Version" I had object named RawSensorMessage which contained many properties and most of them were empty at most cases. So when I moved into Mongo I changed RawSensorMessage definition to the following one:
[DataContract]
public class RawSensorMessage
{
[DataMember]
public Dictionary<string, object> Data { get; set; }
}
Assume Inside Mongo I have a collection with many records which each one contains different properties.
When I query this collection, I don't want to map it to a strongly type object, I want to query it to a 'RawSensorMessage' object (means to fill the 'Data' dictionary inside with the properties and values).
How should I do it?
Is it accepted approach to work with structure-less collections?

A solution would be to cast your BSON document which you get from the MongoDB to an JSON object: ToJSON() After that you can serialize your JSON object to a dictionary, like this:
var result = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
Hope this help.

Related

How to find and replace values in nested JSON dynamically

I have a service call that accepts the following model:
public class ServiceModel {
public DataModel dModel {get; set;}
public JObject schema {get; set;}
}
The DataModel is responsible for holding data that will be used to populate user defined schema (see below).
The schema is a user defined (at runtime) dynamic json structure that contains tokenized values like this. Being that it's user defined, it can be deeply nested.
{
"id": "<tokenized_id>",
"hero":{
"heroName": "<tokenized_heroName>",
"heroType": "<tokenized_heroType>",
"heroSkill": "<tokenized_heroSkill>",
"heroArmor": {
"armor_id": "<tokenized_armorId>",
...
}
}
}
What I want to do is pull data from the DataModel and replace the corresponding tokenized value with it. The tricky part comes from the possibility of deeply nested objects
My first idea is to just flatten the schema into a string and doing a Find/Replace on the entire string but I'm curious if there's a more elegant way.
There's the option of working with JObjects or even Dictionary but neither provide a nice way to access nested objects. I believe I would need to use recursion which could get ugly.
Are there betters ways to accomplish this?

Convert JSON to List of OrderedDictionaries, then Add() back to new List

I'm attempting to read the contents of a JSON file using Newtonsoft JSON which is a list of dictionaries, iterate through them, and create a new list of dictionaries after rooting out the one I don't want which will eventually be written back to the JSON file.
No matter what I try, I can't seem to be able to add the JSON entries within its list back to a new List. Here's the error:
Unhandled Exception: Microsoft.CSharp.RuntimeBinder.RuntimeBinderException:
The best overloaded method match for 'System.Collections.Generic.List<System.Collections.Specialized.OrderedDictionary>.Add(System.Collections.Specialized.OrderedDictionary)'
has some invalid arguments
Here's the JSON string I've deserialized:
[
{
"name":"test",
"custom":false,
"file":"strawberry-perl-5.10.1.2portable.zip",
"url":"http://strawberryperl/....",
"ver":"5.10.1",
"csum":"f86ae4b14daf0b1162d2c4c90a9d22e4c2452a98"
}
]
And here's my code:
dynamic customPerlList = JsonParse("perls_custom");
List<dynamic> updatedList = new List<dynamic>();
foreach (var perlStruct in customPerlList)
{
if (perlStruct.name != perlVersionToRemove)
{
Console.WriteLine("match");
updatedList.Add((OrderedDictionary)perlStruct);
}
}
I've just started developing in C#, so my attempts using examples found while searching either aren't sinking in, or I'm missing something else. Can someone please point out the err in my ways, and what the proper approach is to do what I'm attempting?
The most likely problem is that typeless JSON objects typically are matched with IDictionary<string, object> interfaces in .NET libraries; OrderedDictionary does not have that interface. And really, JSON objects are not considered ordered.
Perhaps you can switch to using a regularDictionary<string, object>, or writing a specific class to serialize to/from.
If you wanted to use Dictionary<string, object>, then you should consider deserializing as follows:
var list = JsonConvert.ToObject<List<Dictionary<string, object>>>(s);

How would I deserialize a json array that contains pairs without keys?

Let's say I have some json similar to the following:
"productdetails": [["loading"], ["loaded"], ["detailkey", "detailvalue"]]
The formatting is out of my control, and I need to be able to access the 'loading' and 'loaded' parts to make sure the data was loaded properly before moving on further to continue work with the data. I haven't been able to figure out how to setup nested properties that also have no key.
Edit: Should have noted, 'loading' and 'loaded' can be different things and there can be varying amounts of status update arrays before the details or other messages. So, the above could be returned, or something like this:
"productdetails": [["loading"],["empty"],["created"],["loaded"],["detailkey", "detailvalue"]]
Edit 2: Excuse the errors in my json syntax, the colons have been switched out to create a properly syntaxed example.
This is just a two-dimensional array, which can be represented as a list of list of string
You should be able to deserialize it to this object:
public class RootObject
{
public List<List<string>> productdetails { get; set; }
}
I recomment Newtonsoft's Json.Net parser.
Example:
string jsonString = "{\"productdetails\": [[\"loading\"], [\"loaded\"], [\"detailkey\", \"detailvalue\"]]}";
RootObject obj = JsonConvert.Deserialize<RootObject>(jsonString);

C# dynamic JSON serialization

I'm calling an API to fetch a list of devices. In my model i have an attribute for list of devices:
public List<Device> device { get; set; }
But, if the API returns 1 device, it's returned as just a Device, not a list of devices with 1 device.
Is there any good way to have a dynamic deserialize? I don't want to have two different models, and parse the JSON programatically just to know which object to deserialize as.
JsonConvert.DeserializeObject<ListDevicesByLabelModel>(responseText);
The dynamic keyword is still great for deserializing JSON, I would recommend that you take a look on this question.
Deserialize JSON into C# dynamic object?
dynamic data = Json.Decode(responseText);
And then you've got a dynamic object to work with instead of needing 2 models.
Otherwise you could also have just one item in the List.

How to return dynamic types List<dynamic> with Dapper ORM

I have been using Dapper.net for a while now and its a very good ORM mapper which works great with .Net dynamic types.
But I noticed that when Dapper retrieves data from a database it returns as DapperRow type.
Is there are any way that I can return it in any other type Like System.Dynamic.ExpandoObject?
Sure!
As per dapper documentation use the query method and get your dymanics:
dynamic account = conn.Query<dynamic>(#"
SELECT Name, Address, Country
FROM Account
WHERE Id = #Id", new { Id = Id }).FirstOrDefault();
Console.WriteLine(account.Name);
Console.WriteLine(account.Address);
Console.WriteLine(account.Country);
As you can see you get a dynamic object and you can access its properties as long as they are well defined in the query statement.
If you omit .FirstOrDefault() you get an IEnumerable<dynamic> which you can do whatever you want with it.
The DapperRow object is designed to share a lot of state between rows. For example, if you fetch 40 rows, the column names etc are only stored once. If we used ExpandoObject, this would need to be configured per row. Hence, the use of DapperRow as the behind-the-scenes implementation detail is a deliberate efficiency thing.
Note that the object returned from the dynamic APIs can also be cast as IDictionary<string,object>.
I would, however, be open to supporting other types that support this dictionary usage - of which ExpandoObject is one. So yes, it could be changed such that:
var rows = conn.Query<ExpandoObject>(...);
works. It simply requires code to support it, and that code does not currently exist. So "no, but perhaps in a future build".
Note also that you don't need to use DapperRow at all... The more expected scenario is to use the generic API to materialize your own types.
I have this problem and I solved by this way!
The Query() function returns a collection of dynamics which underneath are actually Dapper.SqlMapper.DapperRow object types. The Dapper.SqlMapper.DapperRow is private. I needed to dynamically add properties to the Dapper.SqlMapper.DapperRow objects but that doesn't appear to work. As a result I wanted to convert the Dapper.SqlMapper.DapperRow into an ExpandoObject.
I was able to build this generic helper method like below.
public class DapperHelpers
{
public static dynamic ToExpandoObject(object value)
{
IDictionary<string, object> dapperRowProperties = value as IDictionary<string, object>;
IDictionary<string, object> expando = new ExpandoObject();
foreach (KeyValuePair<string, object> property in dapperRowProperties)
expando.Add(property.Key, property.Value);
return expando as ExpandoObject;
}
}
Then you can use that like this:
IEnumerable<ExpandoObject> result =
db.SqlConn.Query(sqlScript)
.Select(x=> (ExpandoObject)ToExpandoObject(x));
reference: dapper-dot-net issues 166

Categories

Resources