What is the difference between PreserveReferencesHandling and ReferenceLoopHandling in Json.Net? - c#

I am looking at one WebAPI application sample that has this coded:
json.SerializerSettings.PreserveReferencesHandling
= Newtonsoft.Json.PreserveReferencesHandling.Objects;
and another with this coded:
json.SerializerSettings.ReferenceLoopHandling
= Newtonsoft.Json.ReferenceLoopHandling.Ignore;
Neither explain why each is chosen. I'm very new to WebAPI, so can someone help by explaining to me in simple terms what the differences are and why I might need to use one over the other.

These settings can best be explained by example. Let's say that we want to represent a hierarchy of employees in a company. So we make a simple class like this:
class Employee
{
public string Name { get; set; }
public List<Employee> Subordinates { get; set; }
}
This is a small company with only three employees so far: Angela, Bob and Charles. Angela is the boss, while Bob and Charles are her subordinates. Let's set up the data to describe this relationship:
Employee angela = new Employee { Name = "Angela Anderson" };
Employee bob = new Employee { Name = "Bob Brown" };
Employee charles = new Employee { Name = "Charles Cooper" };
angela.Subordinates = new List<Employee> { bob, charles };
List<Employee> employees = new List<Employee> { angela, bob, charles };
If we serialize the list of employees to JSON...
string json = JsonConvert.SerializeObject(employees, Formatting.Indented);
Console.WriteLine(json);
...we get this output:
[
{
"Name": "Angela Anderson",
"Subordinates": [
{
"Name": "Bob Brown",
"Subordinates": null
},
{
"Name": "Charles Cooper",
"Subordinates": null
}
]
},
{
"Name": "Bob Brown",
"Subordinates": null
},
{
"Name": "Charles Cooper",
"Subordinates": null
}
]
So far so good. You'll notice, however, that the information for Bob and Charles is repeated in the JSON because the objects representing them are referenced both by the main list of employees and Angela's list of subordinates. Maybe that's OK for now.
Now suppose we'd also like to have a way to keep track of each Employee's supervisor in addition to his or her subordinates. So we change our Employee model to add a Supervisor property...
class Employee
{
public string Name { get; set; }
public Employee Supervisor { get; set; }
public List<Employee> Subordinates { get; set; }
}
...and add a couple more lines to our setup code to indicate that Charles and Bob report to Angela:
Employee angela = new Employee { Name = "Angela Anderson" };
Employee bob = new Employee { Name = "Bob Brown" };
Employee charles = new Employee { Name = "Charles Cooper" };
angela.Subordinates = new List<Employee> { bob, charles };
bob.Supervisor = angela; // added this line
charles.Supervisor = angela; // added this line
List<Employee> employees = new List<Employee> { angela, bob, charles };
But now we have a bit of a problem. Because the object graph has reference loops in it (e.g. angela references bob while bob references angela), we will get a JsonSerializationException when we try to serialize the employees list. One way we can get around this issue is by setting ReferenceLoopHandling to Ignore like this:
JsonSerializerSettings settings = new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
Formatting = Formatting.Indented
};
string json = JsonConvert.SerializeObject(employees, settings);
With this setting in place, we get the following JSON:
[
{
"Name": "Angela Anderson",
"Supervisor": null,
"Subordinates": [
{
"Name": "Bob Brown",
"Subordinates": null
},
{
"Name": "Charles Cooper",
"Subordinates": null
}
]
},
{
"Name": "Bob Brown",
"Supervisor": {
"Name": "Angela Anderson",
"Supervisor": null,
"Subordinates": [
{
"Name": "Charles Cooper",
"Subordinates": null
}
]
},
"Subordinates": null
},
{
"Name": "Charles Cooper",
"Supervisor": {
"Name": "Angela Anderson",
"Supervisor": null,
"Subordinates": [
{
"Name": "Bob Brown",
"Subordinates": null
}
]
},
"Subordinates": null
}
]
If you examine the JSON, it should be clear what this setting does: any time the serializer encounters a reference back to an object it is already in the process of serializing, it simply skips that member. (This prevents the serializer from getting into an infinite loop.) You can see that in Angela's list of subordinates in the top part of the JSON, neither Bob nor Charles show a supervisor. In the bottom part of the JSON, Bob and Charles both show Angela as their supervisor, but notice her subordinates list at that point does not include both Bob and Charles.
While it is possible to work with this JSON and maybe even reconstruct the original object hierarchy from it with some work, it is clearly not optimal. We can eliminate the repeated information in the JSON while still preserving the object references by using the PreserveReferencesHandling setting instead:
JsonSerializerSettings settings = new JsonSerializerSettings
{
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
Formatting = Formatting.Indented
};
string json = JsonConvert.SerializeObject(employees, settings);
Now we get the following JSON:
[
{
"$id": "1",
"Name": "Angela Anderson",
"Supervisor": null,
"Subordinates": [
{
"$id": "2",
"Name": "Bob Brown",
"Supervisor": {
"$ref": "1"
},
"Subordinates": null
},
{
"$id": "3",
"Name": "Charles Cooper",
"Supervisor": {
"$ref": "1"
},
"Subordinates": null
}
]
},
{
"$ref": "2"
},
{
"$ref": "3"
}
]
Notice that now each object has been assigned a sequential $id value in the JSON. The first time that an object appears, it is serialized in full, while subsequent references are replaced with a special $ref property that refers back to the original object with the corresponding $id. With this setting in place, the JSON is much more concise and can be deserialized back into the original object hierarchy with no additional work required, assuming you are using a library that understands the $id and $ref notation produced by Json.Net / Web API.
So why would you choose one setting or the other? It depends on your needs of course. If the JSON will be consumed by a client that does not understand the $id/$ref format, and it can tolerate having incomplete data in places, you would choose to use ReferenceLoopHandling.Ignore. If you're looking for more compact JSON and you will be using Json.Net or Web API (or another compatible library) to deserialize the data, then you would choose to use PreserveReferencesHandling.Objects. If your data is a directed acyclic graph with no duplicate references then you don't need either setting.

The explaination is perfect. For me the below one worked, data is your object.
However, if the above-mentioned method did not work, you can try this one:
string json = JsonConvert.SerializeObject(data, Formatting.Indented,new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
});

Related

How can I access my nested data using variables C#

I have a json file and I deserialised it as shown in the code below. Some context, dex is a robot and it has information such as battery and so on as shown in the json file below. I want to read the battery status for the robot that the user selected ( robot).
This is my code, currently im only accessing data.dex but i want to change it to whichever data.robot, where robot is a variable
var data = JsonConvert.DeserializeObject<Root>(json);
var robot = queryDetails2.Amr;
var text =$"";
if (data.dex.battery.status == "good")
{
text = $"{queryDetails2.Amr}'s battery is in good condition. ";
}
This is the json file:
{
"dex":
{
"current_job":
{"job":null, "task": null, "location": null},
"battery":
{"name":"battery", "status": "good", "value": "100"},
},
"dex_1":
{
"current_job":
{"job":null, "task": null, "location": null},
"battery":
{"name":"battery", "status": "good", "value": "100"},
},
"dex_2":
{
"current_job":
{"job":null, "task": null, "location": null},
"battery":
{"name":"battery", "status": "good", "value": "100"},
}
}
I wanted to use the GetMethod or the solution as suggested in this question (https://stackoverflow.com/questions/53076743/how-to-access-variable-of-a-nested-functions-in-python[1]).
However, im getting errors like it does not have a method. Now im confused, was it because i used var? but the deserialised method converts the json to an object though..
Anyway, how should i approach this?
Assuming that you have 3 robots with different names: dex, dex_1 and dex_2, you should reorganize your solution to treat the json data as a list of Robots instead of 3 separate variables for each robot.
To do this, first your json should look like this:
{
"robots":[
{
"name":"dex",
"current_job":{
"job":null,
"task":null,
"location":null
},
"battery":{
"name":"battery",
"status":"good",
"value":"100"
}
},
{
"name":"dex_1",
"current_job":{
"job":null,
"task":null,
"location":null
},
"battery":{
"name":"battery",
"status":"good",
"value":"100"
}
},
{
"name":"dex_2",
"current_job":{
"job":null,
"task":null,
"location":null
},
"battery":{
"name":"battery",
"status":"good",
"value":"100"
}
}]
}
Next, update your serialization classes. Add a field called name in the Robot class or whatever class type you currently have for data.dex. In Root, remove the "dex" fields and add a List<Robot>.
public class Root
{
public List<Robot> robots { get; set; }
}
public class Robot
{
public string name { get; set; }
public Job current_job { get; set;}
public Battery battery{ get; set; }
}
Now you can write whatever logic to get the right robot data. Here is an example using System.Linq:
var robotName = "dex_2";
var robotInfo = data.robots.First(x => x.name.Equals(robotName));
var text = $"{robotName}'s battery is in {robotInfo.battery.status} condition.";

Strongly Typed IDs and JSON Schema generation in C#

I like the idea of Strongly Typed IDs and I am using it for some time. I use it also in DTOs without and major problems... till now. I have a use case where I need to generate JSON Schema from some classes which uses Strongly Type IDs like following:
[StronglyTypedId(StronglyTypedIdBackingType.Long)]
public partial struct TweetId
{
}
public class Tweet
{
public TweetId Id {get; set;}
}
So, as described, TweetID is represented as long base type.
The problem arises when I want to generate JSON Schema from this type. I am currently using Newtonsoft.Json.Schema (I also tried some others) using this code:
JSchemaGenerator generator = new JSchemaGenerator();
JSchema quotesSchema = generator.Generate(typeof(Tweet));
The result I get is:
{
"definitions": {
"Tweet": {
"type": [
"object"
],
"properties": {
"Id": {
"$ref": "#/definitions/TweetId"
}
}
},
"TweetId": {},
},
"type": "object",
"$ref": "#/definitions/Tweet"
}
But what I want is:
{
"definitions": {
"Tweet": {
"type": [
"object"
],
"properties": {
"Id": {
"type": "integer"
}
}
}
},
"type": "object",
"$ref": "#/definitions/Tweet"
}
Do you have any idea how to achive this? Newtonsoft.Json.Schema is not mendatory.

Parsing through complex json in C#

I am attempting to parse through the following to determine the Name and Goals of the top scoring Player for the specific "AC Milan" in this json file that contains many other teams and countries. The main issue I have been running into when I parse it as a JObject is the inability to parse a JObject as a JArray as this json file is pretty messy.
Unhandled exception. System.InvalidCastException: Unable to cast object of type 'Newtonsoft.Json.Linq.JArray' to type 'Newtonsoft.Json.Linq.JObject'.
you have a json array, so you have to use for parsing JArray, not JObject
var jsonParsed=JArray.Parse(json);
and IMHO you posted an invalid json, it needs "{" before "AC Milan". So it should be like this
[
{
"Italy": [
{
"SerieA": [
{
"ACMilan": [
{
"Name": "Player1",
"Goals": 3
},
{
"Name": "Player2",
"Goals": 5
}
]
}
]
}
]
}
]
in this case you can use this query
List<Score> milanPlayersList = JArray.Parse(json)
.Select(i=> ((JObject) i).Properties().FirstOrDefault(x => x.Name=="Italy" ).Value).First()
.Select(i=> ((JObject) i).Properties().FirstOrDefault(x => x.Name=="SerieA" ).Value).First()
.Select(i=> ((JObject) i).Properties().FirstOrDefault(x => x.Name=="ACMilan" ).Value).First()
.Select(i=> i.ToObject<Score>()).ToList().Dump();
using this list you can make any kind of queries.
result
[
{
"Name": "Player1",
"Goals": 3
},
{
"Name": "Player2",
"Goals": 5
}
]
class
public class Score
{
public string Name { get; set; }
public int Goals {get; set;}
}

How can I map REST entities with my own models

I have been given a REST service which provides some strange names, see json fomart below.
I have created some classes to map them to JSON using the following line:
var data = JsonConvert.DeserializeObject<SSTR)(jsond).inhoud_b.ToArray();
I have created classes such as:
Class inhoud_b {...}
Class bidt{...}
etc.
Json:
{
"nmbr": 0,
"tp": 1,
"sort": {
"sorted": false,
"unsorted": true
},
"inhoud_b":[
{
"bidt": {
"id": "144a207b-dd48-4ebe-87cb-153e3c6179c4",
"knldge": null,
"lTT": null,
"lD": {
"id": "52",
"text": "EM"
}
}
},
{
"bidt": {
"id": "155B307b-dd48-4ebe-87cb-125487856579",
"knldge": null,
"lTT": null,
"lDg": {
"id": "523",
"text": "AM"
}
}
}
],
"first": true,
"totalElements": 2
}
My question is, how can I map these entities with my own more meaningful models and use my models in my own code rather than those entities which don't make any sense to other developers?
Thank you.
You can use the JsonPropertyAttribute to annotate your model properties with their corresponding property name in the JSON - https://www.newtonsoft.com/json/help/html/JsonPropertyName.htm

How do I generate specific JSON output from VB.Net?

Being a VB.Net programmer using VB.Net 2015 community, I come across items in C# that I need to convert to VB, but this time I don't understand what I'm working with. The website service I'm consuming returns and expects JSON / JOBJECTS structures like:
var token = new {
iss = PARTNERID,
product = "twpemp",
sub = "partner",
siteInfo = new {
type = "id",
id = SITEID
},
exp = (Int32)DateTime.UtcNow.Add(new TimeSpan(0, 4, 30)).Subtract(new DateTime(1970, 1, 1)).TotalSeconds
};
An online converter converted this into:
Dim EPochTime = DateTime.UtcNow.Add(New TimeSpan(0, 4, 0)).Subtract(New DateTime(1970, 1, 1)).TotalSeconds
Dim Token = New With {
Key .iss = AccNumber,
Key .product = "twppartner",
Key .sub = "partner",
Key .siteInfo = New With {
Key .type = "id",
Key .id = Site},
Key .exp = EPochTime
}
I need to dynamically create this type of structures because the "key name" and values change depending on what was returned and needs to be sent back. For example, depending on the siteid from above, the returning structure might have stuff like:
"Results": [
{
"RecordNumber": 000001,
"EmployeeCode": "0001",
"FirstName": "John",
"MiddleName": "A",
"LastName": "Dow",
"Designation": "Worker",
"Home1": "Press",
},
{
"RecordNumber": 000002,
"EmployeeCode": "0002",
"FirstName": "Jane",
"MiddleName": "b",
"LastName": "Dow",
"Designation": "Helper",
"Home1": "Office",
}
}
For the next client I submit a query for, and eventually need to update with might have:
"Results": [
{
"RecordNumber": 12345,
"EmployeeCode": "231",
"FirstName": "Erick",
"MiddleName": "G",
"LastName": "Smith",
"Department": "Electrial",
},
{
"RecordNumber": 732456,
"EmployeeCode": "853",
"FirstName": "Fred",
"MiddleName": "W",
"LastName": "Kerber",
"Department": "Electrial",
}
}
The difference between the two is one has "Department" and the other doesn't. This structure changes based on the siteID from the first call.
My main question is how do I create something like this dynamically in VB.NET, and secondarily, exactly what is this type of thing called? I'm calling it a structure for lack of better words.
If you want a little more flexibility with outputting this stuff in JSON, there are two approaches:
1) You can use Dictionary<string,object> instead of dynamic types. With the dictionary approach, you can add (or exclude) properties at run-time. The JSON serializer will output in the same fashion as if you were serializing a dynamic type.
var dict = new Dictionary<string, object>() { { "key1", "value1"} };
dict["key2"] = DateTime.Now();
dict["key3"] = 1234567;
if (someCondition){
dict["key4"] = new Dictionary<string, object>() { { "key5", "value5"}, { "key6", "value6"}};
}
2) You can create a class that has ALL the available properties that the JSON structure might include. For optional properties which are numeric types, make them nullable:
public class Qwijibo
{
public int? RecordNumber {get;set;}
public string EmployeeCode {get;set;}
public string FirstName {get;set;}
public string MiddleName {get;set;}
public string LastName {get;set;}
public string Designation {get;set;}
public string Home1 {get;set;}
public string Department {get;set;
}
The above class may work in both the scenarios you presented. Properties you don't assign a value to will serialize as null in JSON. As long as whatever you're transmitting to doesn't get hung up on null values, you're good to go.

Categories