C# / MongoDB Deserialization of Array of Array of Dictionaries - c#

I am attempting to create an array of array of dictionaries in ASP.NET Core using MongoDB. In the end, the data should retain this model for all uses:
Model
{
"ContextName": "Base rates",
"Indexes": [
{
"IndexName": "S&P",
"IndexValues" : [
{
"Date": "2019-01-01",
"Value": "2600.98"
},
{
"Date": "2019-01-02",
"Value": "2605.98"
}
]
}
]
}
Currently, I have my first layer defined below encompassing the ContextName and Indexes:
Context.cs
public class Context
{
[BsonId]
[BsonRepresentation(MongoDB.Bson.BsonType.ObjectId)]
public string Id { get; set; }
[BsonElement("ContextName")]
public string ContextName { get; set; }
[BsonElement("Indexes")]
public ICollection<Index> Indexes { get; set; }
}
With Index having the following structure:
Index.cs
public class Index
{
[BsonElement("IndexName")]
public string IndexName { get; set; }
[BsonElement("IndexValues")]
[BsonDictionaryOptions(DictionaryRepresentation.ArrayOfDocuments)]
public Dictionary<DateTime, double> IndexValues { get; set; }
}
Running this code I am met with a Format Exception stating
An error occurred while deserializing the IndexValues property of
class Database.Index: Invalid element: 'Date'.'
My first thought is that it is an issue with the key being of type Date and not of string within the database itself, however I have read here towards the bottom that the official driver supports this actually.
I went a bit further to try and set some custom { get; set; } to bring in the key as a string but am being met with the same error in the end.
public Dictionary<DateTime, double> IndexValues
{
get
{
Dictionary<DateTime, double> _indexValues = new Dictionary<DateTime, double>();
foreach (var indexValue in IndexValues)
{
_indexValues.Add(indexValue.Key, indexValue.Value);
}
return _indexValues;
}
set { IndexValues = IndexValues; } // Unsure about here as well
}
I am unsure what to be looking at for this case, and am looking for some pointers to figure this out. Thanks in advance.
Update
Formatting the dictionary in an external method allows for the correct format, however also inserts one extra field as it is used in gathering all the required information.
public Dictionary<DateTime, double> IndexValuesDictionary
{
get { return SetDictionary(); }
set { SetDictionary(); }
}
private Dictionary<DateTime, double> SetDictionary()
{
if (_indexValues == null)
{
_indexValues = new Dictionary<DateTime, double>();
foreach (var indexValue in IndexValues)
{
_indexValues.Add(indexValue.Date, indexValue.Value);
}
}
return _indexValues;
}
{
"id": "5c93c1123369220b685719b3",
"contextName": "Base rates",
"indexes": [
{
"indexName": "S&P",
"indexValues": [
{
"date": "2019-01-01T00:00:00Z",
"value": 2600.98
},
{
"date": "2019-01-02T00:00:00Z",
"value": 2605.98
}
],
"indexValuesDictionary": { // This should not be included
"2019-01-01T00:00:00Z": 2600.98,
"2019-01-02T00:00:00Z": 2605.98
}
}
]
}
Is this the only viable option for this output?

After taking a step back and looking at the data structure, I was able to get this working by removing the array nesting and working solely with document nesting (unfortunate but simplifies the schema). The updated database structure is:
[
{
"ContextName": "Base rates",
"Description": "Base rates description",
"Indexes": {
"SPX": {
"2019-01-01T00:00:00Z": 2600.98
},
"NDX": {
"2019-01-01T00:00:00Z": 6600.98
}
}
}
]
Leaving the class structures updated as:
Context.cs
public class Context
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
[BsonElement("ContextName")]
public string ContextName { get; set; }
[BsonElement("Description")]
public string Description { get; set; }
[BsonElement("Indexes")]
[BsonDictionaryOptions(DictionaryRepresentation.Document)]
public Index Indexes { get; set; }
}
Index.cs
public class Index : Dictionary<string, Dictionary<DateTime, double>> { }
I also changed the serializer to treat DateTime as a string instead, and so included this in the program startup:
BsonSerializer.RegisterSerializer(new DateTimeSerializer(DateTimeKind.Local, BsonType.String));
These changes allow the structure to be formatted correctly.

Related

How do I access specific data from a complex model and map it to another model in C#?

I'm looking for some help with mapping data between two models. Whilst mapping isn't particularly difficult, the model I've been provided with by my customer is the one I have to use but it's structure is quite frustrating when trying to map between the two models.
I need to map between HireScedule and MyHireSchedule.
Customer Model
HireSchedule.cs
namespace MyProject.Company.Models.HireSchedule
{
public class AvailableHires
{
public NameValueModel[] HireSpecifics { get; set; }
}
}
NameValueModel.cs
namespace MyProject.Company.Base
{
public class NameValueModel
{
public string Name { get; set; }
public ValueModel Value { get; set; }
}
}
ValueModel.cs
namespace MyProject.Company.Base
{
public class ValueModel
{
public string Value { get; set; }
}
}
My model
namespace MyProject.Company.Models.MyHireSchedule
{
public class AvailableHiresResponse
{
public string Start { get; set; }
public string End { get; set; }
}
}
Here is an example response from the customer model.
"hireSchedule": [
{
"hireSpecifics": [
{
"name": "HireDate",
"value": [
{
"value": "2022-01-01"
}
]
},
{
"name": "HireStart",
"value": [
{
"value": "08:00"
}
]
},
{
"name": "HireEnd",
"value": [
{
"value": "17:30"
}
]
}
]
},
So, now that we know the model setup and the response coming from the customer model, I now need to map that into my model. The problem comes from the name/value pairing they've used. It's hard to be specic when mapping.
This is my first attempt which doesn't work due to the model structure.
public async Task<IEnumerable<MyHireSchedule>> MyFunction()
{
return data.Select(d => new MyHireSchedule
{
Start = d.HireSpecifics.Select(x => x.Value.Value.Where(x.Name.Equals("HireStart"))),
End = d.HireSpecifics.Select(x => x.Value.Value.Where(x.Name.Equals("HireEnd"))),
}).ToArray();
}
My question is, given the structure of the model, how can I define exactly what value should be posted against each of my own model properties. My selection attempt throws errors and for obvious reasons. Is there a better way I can be doing this?

deserialize then serialize json with dynamic field name

I have a JSON file that looks something like this.
{
"id": "top1",
"components": [
{
"type": "resistor",
"id": "res1",
"resistance": {
"default": 100,
"min": 10,
"max": 1000
},
"netlist": {
"t1": "vdd",
"t2": "n1"
}
},
{
"type": "nmos",
"id": "m1",
"m(l)": {
"default": 1.5,
"min": 1,
"max": 2
},
"netlist": {
"drain": "n1",
"gate": "vin",
"source": "vss"
}
}
]
}
I want to make an API using oop to work with that JSON file,
I made the following classes.
public class Topology
{
[Required]
public string id { get; set; }
[Required]
public List<TopologyComponents> components { get; set; }
}
public class TopologyComponents
{
[Required]
public string type { get; set; }
[Required]
public string id { get; set; }
[Required]
public Values ???????? {get; set; }
[Required]
public Dictionary<string, string> netlist { get; set; }
}
public class Values
{
[Required]
public double #default { get; set; }
[Required]
public double min { get; set; }
[Required]
public double max { get; set; }
}
my question is those question marks????????
the field name is dynamic resistance, m(l), .....
how can I handle those cases?
I tried Jackson annotations, expandobject, and dictionaries. but none of them worked as I want.
From the looks of it, your Topology class will need to have Dictionary<string, dynamic> data type since the keys of components will be arbitrary. Even though type and id will be the same across all components, netlist and one other property will be dynamic.
Change your list of components to Dictionary<string, dynamic> and then get the data you need by first checking which property actually exists in the component.
public class Topology
{
[Required]
public string id { get; set; }
[Required]
public List<Dictionary<string, dynamic>> components { get; set; }
}
this will give you a list of dictionary with string as the keys and dynamic objects as the values. You can iterate through the keys using foreach loop on components.Keys and perhaps a switch statement to see if the key you expect exists when iterating through each component.
Example on how you can create your own list of components... not sure how you are going to use the data since that will drive the way you deserialize this,
var obj = JsonConvert.DeserializeObject<Topology>(jsonText);
List<dynamic> allComps = new List<dynamic>();
foreach(var component in obj.components)
{
var comp = new ExpandoObject() as IDictionary<string, object>;
foreach(var key in component.Keys)
{
switch (key)
{
case "id":
case "type":
comp.Add(key, component[key].ToString());
break;
case "netlist":
comp.Add(key, component[key].ToObject<Dictionary<string, string>>());
break;
default:
comp.Add(key, component[key].ToObject<Values>());
break;
}
}
allComps.Add(comp);
}

Deserialize Active campaign's beautiful json output to c# object

...and "beautiful" is sarcastic here.
When you call Active Campaign's list_view endpoint, and would like to get that in a json response, then this is the json response you get:
{
"0": {
"id": "4",
"name": "Nieuwsletter 1",
"cdate": "2018-11-22 03:44:19",
"private": "0",
"userid": "6",
"subscriber_count": 2901
},
"1": {
"id": "5",
"name": "Newsletter 2",
"cdate": "2018-11-22 05:02:41",
"private": "0",
"userid": "6",
"subscriber_count": 2229
},
"2": {
"id": "6",
"name": "Newsletter 3",
"cdate": "2018-11-22 05:02:48",
"private": "0",
"userid": "6",
"subscriber_count": 638
},
"result_code": 1,
"result_message": "Success: Something is returned",
"result_output": "json"
}
Now how would I ever be able to deserialize this to an object? Doing the normal Edit => Paste Special => Paste JSON As Classes gives me an output where I end up with classes that named _2.
Also, JsonConvert throws the following error: Accessed JObject values with invalid key value: 2. Object property name expected. So it is not really able to deserialize it either. I tried to use dynamic as object type to convert to.
The only thing I can think of now is replacing the first { by [ and the last } by ], then remove all the "1" : items and then remove the last 3 properties. After that I have a basic array which is easy convertable. But I kind of hope someone has a better solution instead of diving deep into the string.indexOf and string.Replace party...
If your key/value pair is not fixed and data must be configurable then Newtonsoft.json has one feature that to be used here and that is [JsonExtensionData]. Read more
Extension data is now written when an object is serialized. Reading and writing extension data makes it possible to automatically round-trip all JSON without adding every property to the .NET type you’re deserializing to. Only declare the properties you’re interested in and let extension data do the rest.
In your case key/value pair with 0,1,2,3.......N have dynamic data so your class will be
So create one property that collects all of your dynamic key/value pair with the attribute [JsonExtensionData]. And below I create that one with name DynamicData.
class MainObj
{
[JsonExtensionData]
public Dictionary<string, JToken> DynamicData { get; set; }
public int result_code { get; set; }
public string result_message { get; set; }
public string result_output { get; set; }
}
And then you can deserialize your JSON like
string json = "Your json here"
MainObj mainObj = JsonConvert.DeserializeObject<MainObj>(json);
Edit:
If you want to collect your dynamic key's value to class then you can use below the class structure.
class MainObj
{
[JsonExtensionData]
public Dictionary<string, JToken> DynamicData { get; set; }
[JsonIgnore]
public Dictionary<string, ChildObj> ParsedData
{
get
{
return DynamicData.ToDictionary(x => x.Key, y => y.Value.ToObject<ChildObj>());
}
}
public int result_code { get; set; }
public string result_message { get; set; }
public string result_output { get; set; }
}
public class ChildObj
{
public string id { get; set; }
public string name { get; set; }
public string cdate { get; set; }
public string _private { get; set; }
public string userid { get; set; }
public int subscriber_count { get; set; }
}
And then you can deserialize your JSON like
MainObj mainObj = JsonConvert.DeserializeObject<MainObj>(json);
And then you can access each of your deserialized data like
int result_code = mainObj.result_code;
string result_message = mainObj.result_message;
string result_output = mainObj.result_output;
foreach (var item in mainObj.ParsedData)
{
string key = item.Key;
ChildObj childObj = item.Value;
string id = childObj.id;
string name = childObj.name;
string cdate = childObj.cdate;
string _private = childObj._private;
string userid = childObj.userid;
int subscriber_count = childObj.subscriber_count;
}
I'd recommend JObject from the Newtonsoft.Json library
e.g. using C# interactive
// Assuming you've installed v10.0.1 of Newtonsoft.Json using a recent version of nuget
#r "c:\Users\MyAccount\.nuget\.nuget\packages\Newtonsoft.Json\10.0.1\lib\net45\Newtonsoft.Json.dll"
using Newtonsoft.Json.Linq;
var jobj = JObject.Parse(File.ReadAllText(#"c:\code\sample.json"));
foreach (var item in jobj)
{
if (int.TryParse(item.Key, out int value))
{
Console.WriteLine((string)item.Value["id"]);
// You could then convert the object to a strongly typed version
var listItem = item.Value.ToObject<YourObject>();
}
}
Which outputs:
4
5
6
See this page for more detail
https://www.newtonsoft.com/json/help/html/QueryingLINQtoJSON.htm

C# Newtonsoft deserialize JSON array

I'm trying to deserialize an array using Newtonsoft so i can display files from a cloud based server in a listbox but i always end up getting this error no matter what i try:
Newtonsoft.Json.JsonReaderException: 'Unexpected character encountered while parsing value: [. Path '[0].priv', line 4, position 15.'
Thisis an example try to deserialize:
[
{
"code": 200,
"priv": [
{
"file": "file.txt",
"ext": "txt",
"size": "104.86"
},
{
"file": "file2.exe",
"ext": "exe",
"size": "173.74"
},
],
"pub": [
{
"file": "file.txt",
"ext": "txt",
"size": "104.86"
},
{
"file": "file2.exe",
"ext": "exe",
"size": "173.74"
}
]
}
]
I tried using a C# Class like this:
public class ListJson
{
[JsonProperty("pub")]
public List List { get; set; }
}
public class List
{
[JsonProperty("file")]
public string File { get; set; }
[JsonProperty("ext")]
public string Ext { get; set; }
[JsonProperty("size")]
public string Size { get; set; }
}
[JsonProperty("priv")]
public List List { get; set; }
}
public class List
{
[JsonProperty("file")]
public string File { get; set; }
[JsonProperty("ext")]
public string Ext { get; set; }
[JsonProperty("size")]
public string Size { get; set; }
}
And deserialize with:
List<list> fetch = Newtonsoft.Json.JsonConvert.DeserializeObject<List<list>>(json);
The correct C# class structure for your JSON is the following:
public class FileEntry
{
public string file { get; set; }
public string ext { get; set; }
public string size { get; set; }
}
public class FileList
{
public int code { get; set; }
public List<FileEntry> priv { get; set; }
public List<FileEntry> pub { get; set; }
}
Deserializing it in this way:
var fetch = JsonConvert.DeserializeObject<FileList[]>(json);
var fileList = fetch.First(); // here we have a single FileList object
As said in the other answer, creating a class called List doesn't automagically turn it into a collection of objects. You need to declare the types to be deserialized from an array a collection type (e.g. List<T>, T[], etc.).
Small tip: when in doubt, use json2csharp.com to generate strongly typed classes from a json string.
At the moment List has a single List instance called priv, which despite the name: doesn't make it a list. To deserialize a JSON array ("priv": [...]), it needs to an array or list-like type, for example List<T> for some T. Presumably a List<FileThing>, if we assume that FileThing is actually the second type called List (you have 2).

How to return complex Json object in asp.net mvc

I am using entity framework to get data from database and serialize it to JSON. And I want my JSON response looks like the one below.
Shoud I add items property to my model and make JSON I want? Thanks.
Desired Json
{
"items" : [
{
"Id": 1,
"AdContent":"Content1"
},
{
"Id": 2,
"AdContent":"Content2"
},
{
"Id": 3,
"AdContent":"Content3"
}
]
}
Current JSON I receive
[
{
"Id":1,
"AdContent":"Content1"
},
{
"Id":2,
"AdContent":"Content2"
},
{
"Id":3,
"AdContent":"Content3"
}
]
{
Controller
public JsonResult GetJson()
{
using (var db = new DoskaUsContext())
{
List<AdViewModel> list = db.Ads.Select(x => new AdViewModel
{
Id = x.AdId,
AdContent = x.AdContent
}).ToList();
return Json(list, JsonRequestBehavior.AllowGet);
}
}
Model
public class AdViewModel
{
public int Id { get; set; }
public string AdContent { get; set; }
}
Anonymous object is one solution Json(new {items=list},...).
General approach to solve that problem - generate strongly typed classes with http://json2csharp.com/ and populate result via generated classes or at least see what is missing from your code.
In this particular case generated code:
public class Item
{
public int Id { get; set; }
public string AdContent { get; set; }
}
public class RootObject
{
public List<Item> items { get; set; }
}
Which shows missing piece of the puzzle - RootObject with list property items.
Create another model which hold collection of AdViewModel as items
public class AdViewModel
{
public int Id { get; set; }
public string AdContent { get; set; }
}
public class NewModel
{
public AdViewModel items { get; set; }
}

Categories

Resources