Serializing JSON Object Array (Json.NET) - c#

I'm learning JSON and was wondering how to create an array of objects. I want my JSON file to look like this
{
"Place": {
"Stores": [{
"Grocery": {
"stock": "fruit",
"distance": 19,
"size": 12
},
"Department": {
"stock": "clothing",
"distance": 21,
"size": 7
}
}]
}
}
Here is what my C# classes looks like
public class RootObject
{
public Place Place { get; set; }
}
public class Place
{
public List<Store> Stores { get; set; }
}
public class Store
{
public Grocery Grocery { get; set; }
public Department Department { get; set; }
}
public class Grocery
{
public string stock { get; set; }
public int distance { get; set; }
public int size { get; set; }
}
public class Department
{
public string stock { get; set; }
public int distance { get; set; }
public int size { get; set; }
}
So far, I have tried coding it like this, similar to how the examples are on the newtonsoft website
Rootobject root = new Rootobject
{
Place = new Place
{
stores = new List<Store>
{
Grocery = new Grocery
{
stock ="fruit",
distance = 19,
size = 12
},
Department = new Department
{
stock ="clothing",
distance = 21,
size = 7
}
}
}
};
string json = JsonConvert.SerializeObject(root,
Formatting.Indented,
new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
System.IO.File.WriteAllText(#"C:\Users\Public\TestFolder\output.json", json);
but I'm getting two CS0117 errors at
Grocery = new Grocery
and
Department = new Department
which says that Store does not contain a definition for Grocery/Department
What am I doing wrong here? Did I just make an error in syntax or is there a possibility I am just approaching serializing this the wrong way? Much thanks in advance for your guy's help.

Your object should look like this:
Rootobject root = new Rootobject
{
Place = new Place
{
stores = new List<Store>
{
new Store{
Grocery = new Grocery
{
stock ="fruit",
distance = 19,
size = 12
},
Department = new Department
{
stock ="clothing",
distance = 21,
size = 7
}
}
}
}
};
I wrote it from my head, so I hope syntax is good. But main idea is that you were creating list of stores and not any store inside that list. You should create some Store using new Store

Related

Deserialize JSON array with unknown keys inside JSON object to a generic property - C#

Finding the right title for this problem was kinda hard so I'll try to explain the problem a bit better below.
I am making a call to an API which returns the following JSON object:
{{
"id": "jsonrpc",
"jsonrpc": "2.0",
"result": {
"result": [
{
"AccountId": 285929,
"Flags": [
"Managed_Obsolete"
],
"PartnerId": 73560,
"Settings": [
{
"AN": "company_1"
},
{
"CD": "1435323320"
},
{
"ED": "2147483647"
},
{
"OS": "Windows Server 2012 R2 Standard Edition (9600), 64-bit"
},
{
"OT": "2"
},
{
"T3": "1085792125772"
},
{
"US": "958222150780"
},
{
"YS": "100"
}
]
},
{
"AccountId": 610474,
"Flags": null,
"PartnerId": 249262,
"Settings": [
{
"AN": "company_2"
},
{
"CD": "1522143635"
},
{
"ED": "2147483647"
},
{
"OS": "Windows 7 Professional Service Pack 1 (7601), 64-bit"
},
{
"OT": "2"
},
{
"T3": "598346102236"
},
{
"US": "758149148249"
},
{
"YS": "100"
}
]
},
],
"totalStatistics": null
},
}}
In above result I listed only the first 2 accounts (total of 80+ accounts normally).
Deserializing the object works fine, I am putting the JSON object fields inside my C# model (list).
The problem however is that I can't get the (inner) Settings array properly in my model. The settings array keys are unknown, I define these keys when I call the API:
JObject requestObject = new JObject();
requestObject.Add(new JProperty("id", "jsonrpc"));
requestObject.Add(new JProperty("jsonrpc", "2.0"));
requestObject.Add(new JProperty("method", "myMethod"));
requestObject.Add(new JProperty("visa", someID));
requestObject.Add(new JProperty("params",
new JObject(
new JProperty("query", new JObject(
new JProperty("PartnerId", partnerId),
new JProperty("StartRecordNumber", 0),
new JProperty("RecordsCount", 9999999),
new JProperty("Columns", new JArray("AR", "AN", "US", "T3", "OT", "OS", "YS"))
)),
new JProperty("timeslice", unixDate),
new JProperty("totalStatistics", "*")
))
);
In above call I define the keys for the Settings array, this could however also be just one key or more. For this reason I want to make my Settings property in my C# model generic (I don't want to list all the possible key names because this are over 100 keys).
What I had so far:
List<EnumerateAccountHistoryStatisticsResult> resultList = new List<EnumerateAccountHistoryStatisticsResult>();
var result = JsonConvert.DeserializeObject<JObject>(streamreader.ReadToEnd());
dynamic innerResult = result["result"]["result"];
foreach (var obj in innerResult)
{
resultList.Add(
new EnumerateAccountHistoryStatisticsResult
{
AccountId = obj.AccountId,
Flags = obj.Flags.ToObject<IEnumerable<string>>(),
PartnerId = obj.PartnerId,
Settings = obj.Settings.ToObject<List<ColumnSettingsResult>>(),
});
}
The EnumerateAccountHistoryStatisticsResult Model:
public class EnumerateAccountHistoryStatisticsResult
{
public int AccountId { get; set; }
public IEnumerable<string> Flags { get; set; }
public int PartnerId { get; set; }
public List<ColumnSettingsResult> Settings { get; set; }
}
The ColumnSettingsResult model:
public class ColumnSettingsResult
{
public string AR { get; set; }
public string AN { get; set; }
public string US { get; set; }
public string T3 { get; set; }
public string OT { get; set; }
public string OS { get; set; }
public string YS { get; set; }
// and list all other columns...
}
With above models I would need to list all the possible columns which are over 100 properties, besides that the result of the Settings list is not logical because I get all the property values but for each different key I get null values:
The ColumnSettingsResult model should more be something like:
public class ColumnSettingsResult
{
public string ColumnName { get; set; }
public string ColumnValue { get; set; }
}
I cant get the key and value inside these two properties though without defining the key name inside the model..
I already tried several things without result (links below as reference).
Anyone that can get me in the right direction?
C# deserialize Json unknown keys
Convert JObject into Dictionary<string, object>. Is it possible?
Convert Newtonsoft.Json.Linq.JArray to a list of specific object type
Try making Settings of type Dictionary<string,string> (or List<KeyValuePair<string,string>> if Dictionary doesn't give you what you want.
public class MyJsonObject
{
public string id { get; set; }
public string jsonrpc { get; set; }
public Result result { get; set; }
public class Result2
{
public int AccountId { get; set; }
public List<string> Flags { get; set; }
public int PartnerId { get; set; }
public Dictionary<string,string> Settings { get; set; } //or List<KeyValuePair<string,string>>
}
public class Result
{
public List<Result2> result { get; set; }
public object totalStatistics { get; set; }
}
}
Then JsonConvert.DerserializeObject<MyJsonObject>(jsonString);

Deserializing this object in JSON.NET

I have the following object:
{
"pickups": {
"7": [
5,
8
],
"10": [
6,
7,
9
],
"15": [
1
],
"20": [
0,
2
],
"25": [
3,
4
]
}
}
I'd like to de-serialize each pickups element into the following object:
public class Pickups {
public Pickup[] pickups;
}
public class Pickup {
public int Group; // This could be the 7, 10, 15, 20, 25, etc.
public int[] Values; // If this was the "7" grouping, it would contain 5, 8.
}
As you can see from the data its a bit tricky to do this. I've been trying to use a JsonConverter to convert the object with a bit of custom code but its been a nightmare and I haven't been able to get it right. I am wondering if anyone would know the best way to convert this type of object into the correct format I need?
While a converter would be a good choice you can still deserialize the Json and construct the desired object graph
var root = JsonConvert.DeserializeObject<RootObject>(json);
var pickups = new Pickups {
pickups = root.pickups.Select(kvp =>
new Pickup {
Group = int.Parse(kvp.Key),
Values = kvp.Value
}
).ToArray()
};
Where
public class RootObject {
public IDictionary<string, int[]> pickups { get; set; }
}
This is what son2csharp.com says, its gets error because you can not define names with starting number.
public class Pickups
{
public List<int> __invalid_name__7 { get; set; }
public List<int> __invalid_name__10 { get; set; }
public List<int> __invalid_name__15 { get; set; }
public List<int> __invalid_name__20 { get; set; }
public List<int> __invalid_name__25 { get; set; }
}
public class RootObject
{
public Pickups pickups { get; set; }
}
But I think
[DataMember(Name = "Name")]
should work cause its not an error in JSON format side.
If it is a viable option for you to use JObject.Parse(...) instead, you could use the following code (and write it more cleanly, with exception handling and safe casts and so on):
var jsonPickups = JObject.Parse(json);
var myPickups = new Pickups
{
pickups = jsonPickups.First.First.Select(x =>
{
JProperty xProp = x as JProperty;
return new Pickup
{
Group = int.Parse(xProp.Name),
Values = (xProp.Value as JArray).Select(y => int.Parse(y.ToString())).ToArray()
};
}).ToArray()
};

MongoDB: query date by year C#

Having such a document:
var document = new BsonDocument
{
{ "address" , new BsonDocument
{
{ "street", "2 Avenue" },
{ "zipcode", "10075" },
{ "building", "1480" },
{ "coord", new BsonArray { 73.9557413, 40.7720266 } }
}
},
{ "borough", "Manhattan" },
{ "cuisine", "Italian" },
{ "grades", new BsonArray
{
new BsonDocument
{
{ "date", new DateTime(2014, 10, 1, 0, 0, 0, DateTimeKind.Utc) },
{ "grade", "A" },
{ "score", 11 }
},
new BsonDocument
{
{ "date", new DateTime(2014, 1, 6, 0, 0, 0, DateTimeKind.Utc) },
{ "grade", "B" },
{ "score", 17 }
}
}
},
{ "name", "Vella" },
{ "restaurant_id", "41704620" }
};
How would I query for grades.date.year = 2016?
Was trying:
var filter = Builders<BsonDocument>.Filter.Eq("grades.date.year", 2016);
var result = await collection.Find(filter).ToListAsync();
But I guess dot notation only works on the json doc, not the objects? Scoured the internet, but couldn't find a clean example.
EDIT: C# classes?
class Address
{
public string street { get; set; }
public string zipcode { get; set; }
public string building { get; set; }
public IEnumerable<double> coord { get; set; }
}
class Grade
{
public DateTime date { get; set; }
public string grade { get; set; }
public int score { get; set; }
}
class TestX
{
public ObjectId _id { get; set; }
public Address address { get; set; }
public string borough { get; set; }
public string cuisine { get; set; }
public IEnumerable<Grade> grades { get; set; }
public string name { get; set; }
public string restaurant_id { get; set; }
}
This requires aggregation framework.
If you could post c# class then will update my answer to strongly typed c# , but now decided to project year inside project phase.
public static void Main()
{
var client = new MongoClient("mongodb://localhost:27017");
var database = client.GetDatabase("test");
var collection = database.GetCollection<BsonDocument>("hammer");
var project =
BsonDocument.Parse(
"{_id: 1,address: 1,borough: 1,cuisine: 1,grades: 1,name: 1,restaurant_id: 1,year: {$year: '$grades.date'}}");
var aggregationDocument =
collection.Aggregate()
.Unwind("grades")
.Project(project)
.Match(BsonDocument.Parse("{'year' : {$in : [2013, 2015]}}"))
.ToList();
foreach (var result in aggregationDocument)
{
Console.WriteLine(result.ToString());
}
Console.ReadLine();
}
edit
var aggregationDocument =
collection.Aggregate<TestX>()
.Unwind<TestX>(x=>x.grades)
.Match(BsonDocument.Parse(
"{$and:[{'grades.date':{$gte: ISODate('2012-01-01')}},{'grades.date':{$lt: ISODate('2013-01-01')}}]}"))
.ToList();
Couldn't another approach be to check if date is within that year.
I.e for 2016 using $gte: 2016-01-01 00:00:00 and $lt: 2017-01-01 00:00:00
Find objects between two dates MongoDB
There are probably functions for them in c# like Gte and Lt. There are probably some easy way to combine the filters like with an & or similar.

Deserializing JSON with Child and Inner Childs

I am familiar with JSON.net a bit and can Deserialize the JSON with basic structure (upto one child). I am currently in process of Deserializing the JSON that is returned from Netatmo API. The structure of JSON is complicated for me. Following is the basic structure of the JSON,
_id
place
location
Dynamic Value 1
Dynamic Value2
altitude
timezone
mark
measures
Dynamic Value 1
res
Dynamic Value 1
Dynamic Value 1
Dynamic Value 2
type
Dynamic Value 1
Dynamic Value 2
modules
Dynamic Value 1
Dynamic Value 1 and Dynamic Value 2 represents the values that is changed for each id. The complete JSON is given below,
{
"body": [{
"_id": "70:ee:50:02:b4:8c",
"place": {
"location": [-35.174779762001, -5.8918476117544],
"altitude": 52,
"timezone": "America\/Fortaleza"
},
"mark": 0,
"measures": {
"02:00:00:02:ba:2c": {
"res": {
"1464014579": [16.7, 77]
},
"type": ["temperature", "humidity"]
},
"70:ee:50:02:b4:8c": {
"res": {
"1464014622": [1018.1]
},
"type": ["pressure"]
}
},
"modules": ["02:00:00:02:ba:2c"]
}, {
"_id": "70:ee:50:12:40:cc",
"place": {
"location": [-16.074257294385, 11.135715243973],
"altitude": 14,
"timezone": "Africa\/Bissau"
},
"mark": 14,
"measures": {
"02:00:00:06:7b:c8": {
"res": {
"1464015073": [26.6, 78]
},
"type": ["temperature", "humidity"]
},
"70:ee:50:12:40:cc": {
"res": {
"1464015117": [997]
},
"type": ["pressure"]
}
},
"modules": ["02:00:00:06:7b:c8"]
}],
"status": "ok",
"time_exec": 0.010364055633545,
"time_server": 1464015560
}
I am confused by looking at the complex structure of this JSON. For single level of JSON I have used this code in the past,
IList<lstJsonAttributes> lstSearchResults = new List<lstJsonAttributes>();
foreach (JToken objResult in objResults) {
lstJsonAttributes objSearchResult = JsonConvert.DeserializeObject<lstJsonAttributes>(objResult.ToString());
lstSearchResults.Add(objSearchResult);
}
But for so many child I have yet to understand how the object class will be created. Any guidance will highly appreciated.
Update:
This is what I have achieved so far.
I have created a main class as below,
public class PublicDataClass
{
public string _id { get; set; }
public PublicData_Place place { get; set; }
public string mark { get; set; }
public List<string> modules { get; set; }
}
and "Place" class is as follow,
public class PublicData_Place
{
public List<string> location { get; set; }
public string altitude { get; set; }
public string timezone { get; set; }
}
Then I have Deserialized the object in the following code line,
var obj = JsonConvert.DeserializeObject<List<PublicDataClass>>(jsonString);
I can now successfully get all the data except the "measures" which is little bit more complicated.
Using json.net, JSON objects that have arbitrary property names but fixed schemas for their values can be deserialized as a Dictionary<string, T> for an appropriate type T. See Deserialize a Dictionary for details. Thus your "measures" and "res" objects can be modeled as dictionaries.
You also need a root object to encapsulate your List<PublicDataClass>, since your root JSON container is an object like so: { "body": [{ ... }] }.
Thus you can define your classes as follows:
public class RootObject
{
public List<PublicDataClass> body { get; set; }
public string status { get; set; }
public double time_exec { get; set; }
public int time_server { get; set; }
}
public class PublicDataClass
{
public string _id { get; set; }
public PublicData_Place place { get; set; }
public int mark { get; set; }
public List<string> modules { get; set; }
public Dictionary<string, Measure> measures { get; set; }
}
public class PublicData_Place
{
public List<double> location { get; set; } // Changed from string to double
public double altitude { get; set; } // Changed from string to double
public string timezone { get; set; }
}
public class Measure
{
public Measure()
{
this.Results = new Dictionary<string, List<double>>();
this.Types = new List<string>();
}
[JsonProperty("res")]
public Dictionary<string, List<double>> Results { get; set; }
[JsonProperty("type")]
public List<string> Types { get; set; }
}
Then do
var root = JsonConvert.DeserializeObject<RootObject>(jsonString);
var obj = root.body;
I've worked with XML for a few years and my change to JSON structure I've got a little confused too, always that I want to see how an object look like I use this web site jsoneditoronline Just copy and paste your JSON and click on arrow to parse to an object, I hope it helps until you get used to JSON structure.

JSON.NET - Object update with List

Could someone suggest a method of updating the items in the Cheese.Producers list?
I have the following classes:
class Producer
{
public string Name { get; set; }
public int Rating { get; set; }
public Producer()
{
}
public Producer(string name, int rating)
{
Name = name;
Rating = rating;
}
}
class Cheese
{
public string Name { get; set; }
public int Age { get; set; }
public string Taste { get; set; }
public List<Producer> Producers { get; set; }
public Cheese()
{
Producers = new List<Producer>();
}
public Cheese(string name, int age)
{
Name = name;
Age = age;
Producers = new List<Producer>();
}
public Cheese(string name, int age, string taste)
{
Name = name;
Age = age;
Taste = taste;
Producers = new List<Producer>();
}
}
In the main code I have an object(gouda) that I want to update based on a JSON read from a file.
static void Main(string[] args)
{
Producer prod1 = new Producer("prod1", 5);
Producer prod2 = new Producer("prod2", 6);
Producer prod3 = new Producer("prod3", 7);
Cheese gouda = new Cheese("Gouda", 5, "Mild");
gouda.Producers.Add(prod1);
gouda.Producers.Add(prod2);
gouda.Producers.Add(prod3);
string propertiesToBeAdded = File.ReadAllText("properties.txt");
JsonSerializerSettings jsonSerializerSettings = new JsonSerializerSettings
{
ObjectCreationHandling = ObjectCreationHandling.Reuse
};
JsonConvert.PopulateObject(propertiesToBeAdded, gouda, jsonSerializerSettings);
}
The JSON update file:
{
"Name": "Hard Blue",
"Taste": "Sharp",
"Producers": [
{
"Name": "prod1",
"Rating": 100
},
{
"Name": "prod3",
"Rating": 300
}
]
}
The major problem is that when the PopulateObject is called, instead of updating the Producers list items, 2 new members are added. The other fields seem to work just fine.
Any suggestions?
Try this:
Producer prod1 = new Producer("prod1", 5);
Producer prod2 = new Producer("prod2", 6);
Producer prod3 = new Producer("prod3", 7);
Cheese gouda = new Cheese("Gouda", 5, "Mild");
gouda.Producers.Add(prod1);
gouda.Producers.Add(prod2);
gouda.Producers.Add(prod3);
var propertiesToBeAdded = File.ReadAllText(#"C:\json path");
var settings = new JsonMergeSettings
{
MergeArrayHandling = MergeArrayHandling.Merge
};
var o1 = JObject.Parse(JsonConvert.SerializeObject(gouda));
o1.Merge(JObject.Parse(propertiesToBeAdded), settings);
var o = o1.ToString();
And you need to change your JSON format a bit :
{
'Name': 'Hard Blue',
'Taste': 'Sharp',
'Producers': [
{
'Name': 'prod1',
'Rating': 100
},
{
},
{
'Name': 'prod3',
'Rating': 300
}
]
}
Here we go:
Hope this helps.
I feel there is no way to accomplish simply what you want to do, with the constraints of the format you're using.
You want to retain unique producers, but are using an array of producers, wich has no way of preserving uniqueness.
So, AFAICT, you have two roads.
1 - you change the producers array in an json object, which would deserialize as a dictionary.
2 - you restrict the use of JSON.NET to the deserialization, and then implement a method "merge" in your Cheese class, with the relevant uniqueness checks.
If you need a refinement over this directions, let me know.
EDIT
First, change your Cheese class into this:
class Cheese
{
public string Name { get; set; }
public int Age { get; set; }
public string Taste { get; set; }
public Dictionary<String, Producer> Producers { get; set; }
public Cheese()
{
Producers = new Dictionary<String, Producer>();
}
public Cheese(string name, int age)
{
Name = name;
Age = age;
Producers = new List<Producer>();
}
public Cheese(string name, int age, string taste)
{
Name = name;
Age = age;
Taste = taste;
Producers = new List<Producer>();
}
}
And your Json accordingly:
{
"Name": "Hard Blue",
"Taste": "Sharp",
"Producers": {
"prod1": {
"Name": "prod1",
"Rating": 100
},
"prod3": {
"Name": "prod3",
"Rating": 300
}
}
}
Everything else should be equal.
The nice thing about the dictionary is that it takes care of the uniqueness of the keys.
I left some redundancy in the data, to simplify the code.
You could remove the
"Name": "prod1",
...
"Name": "prod3",
lines from the Json, populating the corresponding name property after deserialization, something like:
foreach(var prod in gouda.Producers.Keys)
{
gouda.Producers[prod].Name = prod;
}
Hope it helps.

Categories