JSON.NET - Object update with List - c#

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.

Related

Randomly picking a value from multiple returns in JSON

This is my code, it works fine for a single return of value, however, what if the results are multiple and I want to pick one randomly?
My code:
class CheckSummonerLevel
{
public class GetVariable
{
public string id { get; set; }
public string secondid { get; set; }
}
public static string Get(string url)
{
var client = new System.Net.WebClient();
string json= client.DownloadString(url);
var result = JsonConvert.DeserializeObject<GetVariable>(json);
string secondid = result.secondid;
return result.id;
}
}
This returns a single value, however, what if my JSON is this:
{
"Values": [
{
"id": 123456
},
{
"id": 987654
},
{
"id": 654987
},
{
"id": 333222
}
],
"secondid": 88888
}
And I want to randomly pick a value from "id" fields?
Something like:
Random a = new Random();
int r = a.Next(1,4);
return result.ElementAt(r).id;
You could modify the structure of GetVariable to match the Json, like the following code :
1 - Class structure :
public class GetVariable
{
public GetId[] Values { get; set; }
public string secondid { get; set; }
}
public class GetId
{
public int id { get; set; }
}
2 - Deserialization of the Json and get one value Randomly :
GetVariable result = JsonConvert.DeserializeObject<GetVariable>(json);
GetId[] values = result.Values;
string secondId = result.secondid;
int r = new Random().Next(0, values.Length);
int res = values.ElementAt(r).id;
Console.WriteLine(res + " ", secondId);
I hope you find this helpful.

Pass Object Array into Request Body

I have an object defined as follows.
public class ILT
{
public items items;
public options options;
}
public class items
{
public string course_code { get; set; }
public string session_code { get; set; }
public string date_name { get; set; }
public string date { get; set; }
public string time_start { get; set; }
public string time_end { get; set; }
public string location_name { get; set; }
public string location_address { get; set; }
public string location_country { get; set; }
public items() { }
public items(string course_code, string session_code, string date_name,
string date, string time_start, string time_end, string location_name,
string location_address, string location_country)
{
this.course_code = course_code;
this.session_code = session_code;
this.date_name = date_name;
this.date = date;
this.time_start = time_start;
this.time_end = time_end;
this.location_name = location_name;
this.location_address = location_address;
this.location_country = location_country;
}
}
I'm trying to pass the object into a RestfulAPI request body. The "items" attribute is supposed to be an array of objects.
The JSon should be formatted as follows:
{
"items": [
{
"course_id": 6,
"session_code": "my session code",
"session_name": "my session name",
"session_maximum_enrollments": 20,
"session_last_subscription_date": "2018-10-27",
"completion_type": "Evaluation",
"score_base": 100,
"date_name": "my date name",
"date": "2018-10-28",
"timezone": "America/New_York",
"time_start": "08:00:00",
"time_end": "12:00:00",
"location_name": "my location name",
"location_address": "10850 W. Park Place Suite 600, Milwaukee, WI 53225",
"location_country": "UNITED STATES OF AMERICA"
}
],
"options": {
"update_session_info": true
}
}
I'm having difficulty getting the items into an array. I'm trying to initialize the object into the request body as follows:
public bool CreateILT()
{
if (String.IsNullOrEmpty(Token))
Token = request.GetToken();
ILT classroom = new ILT
{
items = new items[0]
(
course_code = "APS_CLASSROOM",
session_code = "APS_CLASSROOM",
date_name = "August 27, 2018",
date = "2018-10-27",
time_start = "08:00:00",
time_end = "17:00:00",
location_name = "Crisis Prevention Institute",
location_address = "10850 W. Park Place Suite 600, Milwaukee, WI 53225",
location_country = "UNITED STATES OF AMERICA"
),
options = new options
{
update_session_info = true
}
};
dynamic response = request.Request_POST("/learn/v1/ilt/session/batch", Token, classroom);
if (response.data.success.ToString() == "True")
success = true;
return success;
}
Am I able to initialize an object array like this? I'm getting errors of various types when tweaking around. The above code errors out on each of the object's member's saying it does not exist in the current context.
Your class variable decleration is wrong. It stores object, not array/list of objects. And I could not see your options class. Do you have it right?
It should be declared as follows:
public class ILT
{
public List<items> items;
public options options;
}
And you should initialize it as follows:
ILT classroom = new ILT
{
items = new List<items> {
new item(
course_code = "APS_CLASSROOM",
session_code = "APS_CLASSROOM",
date_name = "August 27, 2018",
date = "2018-10-27",
time_start = "08:00:00",
time_end = "17:00:00",
location_name = "Crisis Prevention Institute",
location_address = "10850 W. Park Place Suite 600, Milwaukee, WI 53225",
location_country = "UNITED STATES OF AMERICA")
},
options = new options
{
update_session_info = true
}
};

Serializing JSON Object Array (Json.NET)

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

How can I create a JsonPatchDocument from comparing two c# objects?

Given I have two c# objects of the same type, I want to compare them to create a JsonPatchDocument.
I have a StyleDetail class defined like this:
public class StyleDetail
{
public string Id { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public decimal OriginalPrice { get; set; }
public decimal Price { get; set; }
public string Notes { get; set; }
public string ImageUrl { get; set; }
public bool Wishlist { get; set; }
public List<string> Attributes { get; set; }
public ColourList Colours { get; set; }
public SizeList Sizes { get; set; }
public ResultPage<Style> Related { get; set; }
public ResultPage<Style> Similar { get; set; }
public List<Promotion> Promotions { get; set; }
public int StoreStock { get; set; }
public StyleDetail()
{
Attributes = new List<string>();
Colours = new ColourList();
Sizes = new SizeList();
Promotions = new List<Promotion>();
}
}
if I have two StyleDetail objects
StyleDetail styleNew = db.GetStyle(123);
StyleDetail styleOld = db.GetStyle(456);
I now want to create a JsonPatchDocument so I can send the differences to my REST API... How to do this??
JsonPatchDocument patch = new JsonPatchDocument();
// Now I want to populate patch with the differences between styleNew and styleOld - how?
in javascript, there is a library to do this https://www.npmjs.com/package/rfc6902
Calculate diff between two objects:
rfc6902.createPatch({first: 'Chris'}, {first: 'Chris', last:
'Brown'});
[ { op: 'add', path: '/last', value: 'Brown' } ]
but I am looking for a c# implementation
Let's abuse the fact that your classes are serializable to JSON!
Here's a first attempt at a patch creator that doesn't care about your actual object, only about the JSON representation of that object.
public static JsonPatchDocument CreatePatch(object originalObject, object modifiedObject)
{
var original = JObject.FromObject(originalObject);
var modified = JObject.FromObject(modifiedObject);
var patch = new JsonPatchDocument();
FillPatchForObject(original, modified, patch, "/");
return patch;
}
static void FillPatchForObject(JObject orig, JObject mod, JsonPatchDocument patch, string path)
{
var origNames = orig.Properties().Select(x => x.Name).ToArray();
var modNames = mod.Properties().Select(x => x.Name).ToArray();
// Names removed in modified
foreach (var k in origNames.Except(modNames))
{
var prop = orig.Property(k);
patch.Remove(path + prop.Name);
}
// Names added in modified
foreach (var k in modNames.Except(origNames))
{
var prop = mod.Property(k);
patch.Add(path + prop.Name, prop.Value);
}
// Present in both
foreach (var k in origNames.Intersect(modNames))
{
var origProp = orig.Property(k);
var modProp = mod.Property(k);
if (origProp.Value.Type != modProp.Value.Type)
{
patch.Replace(path + modProp.Name, modProp.Value);
}
else if (!string.Equals(
origProp.Value.ToString(Newtonsoft.Json.Formatting.None),
modProp.Value.ToString(Newtonsoft.Json.Formatting.None)))
{
if (origProp.Value.Type == JTokenType.Object)
{
// Recurse into objects
FillPatchForObject(origProp.Value as JObject, modProp.Value as JObject, patch, path + modProp.Name +"/");
}
else
{
// Replace values directly
patch.Replace(path + modProp.Name, modProp.Value);
}
}
}
}
Usage:
var patch = CreatePatch(
new { Unchanged = new[] { 1, 2, 3, 4, 5 }, Changed = "1", Removed = "1" },
new { Unchanged = new[] { 1, 2, 3, 4, 5 }, Changed = "2", Added = new { x = "1" } });
// Result of JsonConvert.SerializeObject(patch)
[
{
"path": "/Removed",
"op": "remove"
},
{
"value": {
"x": "1"
},
"path": "/Added",
"op": "add"
},
{
"value": "2",
"path": "/Changed",
"op": "replace"
}
]
You could use my DiffAnalyzer. It's based on reflection and you can configure the depth you want to analyze.
https://github.com/rcarubbi/Carubbi.DiffAnalyzer
var before = new User { Id = 1, Name="foo"};
var after= new User { Id = 2, Name="bar"};
var analyzer = new DiffAnalyzer();
var results = analyzer.Compare(before, after);
You can use this
You can install using NuGet, see SimpleHelpers.ObjectDiffPatch at NuGet.org
PM> Install-Package SimpleHelpers.ObjectDiffPatch
Use:
StyleDetail styleNew = new StyleDetail() { Id = "12", Code = "first" };
StyleDetail styleOld = new StyleDetail() { Id = "23", Code = "second" };
var diff = ObjectDiffPatch.GenerateDiff (styleOld , styleNew );
// original properties values
Console.WriteLine (diff.OldValues.ToString());
// updated properties values
Console.WriteLine (diff.NewValues.ToString());

LINQ grouping in object

I have two classes
public class MyObjects{
public bool Active {get; set;}
public List<OtherObject> OtherObjects {get; set;}
}
public class OtherObject {
public int Id {get; set;}
public bool Enabled {get; set;}
public string Address {get; set;}
public string Name {get; set;}
}
My current result is
MyObject { Active = true; },
OtherObjects: [OtherObject: { Id: 1, Name: 'First'},
OtherObject{Id: 2, Name: 'First'},
OtherObject{Id: 3, Name: 'Second'}];
I want to group them by Name so I would still have Active property and those OtherObjects inside would be grouped by OtherObject Name property. Is it possible to do so only using LINQ?
EDIT:
Final result should be json, that I will use in angular, so it should be something like this:
{
""Active"": true,
""OtherObjects"": [
{
""ObjectName"": ""Second"",
""ObjectOtherProperties"": [
{
""Id"": 1,
""Enabled"": false
},
{
""Id"": 2,
""Enabled"": true
}
],
""ObjectName"": ""Second"",
""ObjectOtherProperties"": [
{
""Id"": 1,
""Enabled"": false
}
],
]
}
}
Any suggestions how to achieve this? Maybe I must make other classes and somehow map them by grouping?
This is how I would do it, keeping it simple:
// 1. Add OtherObjectsDictionary
// 2. Block OtherObjects in the json serialization
public class MyObjects
{
public bool Active { get; set; }
[Newtonsoft.Json.JsonIgnore]
public List<OtherObject> OtherObjects { get; set; }
public Dictionary<string, List<OtherObject>> OtherObjectsDictionary { get; set; }
}
// 3. Block Name in the json serialization
public class OtherObject
{
public int Id { get; set; }
public bool Enabled { get; set; }
public string Address { get; set; }
[Newtonsoft.Json.JsonIgnore]
public string Name { get; set; }
}
// 4. Linq queries to achieve the grouped result
// 5. Serialize to Json
static void Main(string[] args)
{
var myObjects = new MyObjects() { Active = true, OtherObjects = new List<OtherObject>() };
myObjects.OtherObjects.Add(new OtherObject { Id = 1, Name = "First" });
myObjects.OtherObjects.Add(new OtherObject { Id = 2, Name = "First" });
myObjects.OtherObjects.Add(new OtherObject { Id = 3, Name = "Second" });
myObjects.OtherObjectsDictionary = new Dictionary<string, List<OtherObject>>();
var distinctNames = myObjects.OtherObjects.Select(otherObject => otherObject.Name).Distinct();
foreach(var distinctName in distinctNames)
{
var groupedObjectsList = myObjects.OtherObjects.Where(otherObject => otherObject.Name == distinctName).ToList();
myObjects.OtherObjectsDictionary.Add(distinctName, groupedObjectsList);
}
var outputJson = Newtonsoft.Json.JsonConvert.SerializeObject(myObjects);
}
This is the json result:
{
"Active": true,
"OtherObjectsDictionary": {
"First": [
{
"Id": 1,
"Enabled": false,
"Address": null
},
{
"Id": 2,
"Enabled": false,
"Address": null
}
],
"Second": [
{
"Id": 3,
"Enabled": false,
"Address": null
}
]
}
}
I hope it helps.
You may also use the System.Web.Extensions .dll as Add References for framework 4.0 projects (not 4.0 Client Profile).
Then add using inside your class.
I also applied a different approach, a more-or-less DB like normalization.
List of classes
public class MyObjects
{
public bool Active { get; set; }
public List<ObjectName> OtherObjects { get; set; }
}
public class ObjectName
{
public string Name { get; set; }
public List<OtherObject> OtherObjectProperties { get; set; }
}
public class OtherObject
{
public int Id { get; set; }
public bool Enabled { get; set; }
[ScriptIgnore]
public string Address { get; set; }
[ScriptIgnore]
public string Name { get; set; }
}
populate the records..
List<OtherObject> oList = new List<OtherObject>();
oList.Add(new OtherObject() { Id = 2, Name = "First" });
oList.Add(new OtherObject() { Id = 3, Name = "Second" });
// each name with objects
List<ObjectName> oNames = new List<ObjectName>();
oNames.AddRange(oList.Select(p => new ObjectName() {
Name = p.Name
, OtherObjectProperties = oList.Where(p1 => p1.Name == p.Name).ToList()
}).Distinct()
);
// parent object with with object names
MyObjects mo = new MyObjects() { Active = true, OtherObjects = oNames };
and finally, the javascript serializer..
JavaScriptSerializer jss = new JavaScriptSerializer();
string b = jss.Serialize(mo);
string b should give you the output like below..
{
"Active":true
,"OtherObjects":[
{
"Name":"First"
,"OtherObjectProperties":[
{
"Id":2
,"Enabled":false}
]},
{
"Name":"Second"
,"OtherObjectProperties":[
{
"Id":3
,"Enabled":false}
]
}]
}
Please advise if you're confused about any of the following.. :)

Categories