Possible to set primary reference? - c#

I've a problem...
I have a class which contains a list of customers and a list with orders and every order holds the customer as reference. If I serialize the object like this I get:
{
"Customers": [
{
"$id": "1",
"FirstName": "A",
"LastName": "B"
}
],
"Orders": [
{
"Owner": {
"$ref": "1"
},
"Desc": "Soup"
}
]
}
That's fine.. but if I remove the customer I get:
{
"Customers": [],
"Orders": [
{
"Owner": {
"$id": "1",
"FirstName": "A",
"LastName": "B"
},
"Desc": "Soup"
}
]
}
But what I wish to have is:
{
"Customers": [],
"Orders": [
{
"Owner": null,
"Desc": "Soup"
}
]
}
The code I used:
[JsonObject(IsReference = true)]
public class Customer
{
public String FirstName;
public String LastName;
}
public class Order
{
public Order(Customer T, String What)
{
Owner = T;
Desc = What;
}
public Customer Owner;
public String Desc;
}
public class Settings
{
public List<Customer> Customers = new List<Customer>();
public List<Order> Orders = new List<Order>();
}
public MainWindow()
{
InitializeComponent();
Settings Data = new Settings();
Customer Customer = new Customer() { FirstName = "A", LastName = "B" };
Data.Customers.Add(Customer);
Order PlaceOrder = new Order(Data.Customers.First(), "Soup");
Data.Orders.Add(PlaceOrder);
Console.WriteLine(JsonConvert.SerializeObject(Data, Formatting.Indented));
Data.Customers.Remove(Customer);
Console.WriteLine(JsonConvert.SerializeObject(Data, Formatting.Indented));
Close();
}
Can someone help me figure out how to do this?

Right now you are only removing the Customer from the list:
Data.Customers.Remove(Customer);
But the Customer still exists and is still referenced by the PlaceOrder. If you debug set a break point after the line above, you can see that Data.Orders[0].Owner still contains the Customer object.
If you don't want the order in the JSON string to point to a customer, then you should set that reference to null:
Data.Orders[0].Owner = null;
// or...
PlaceOrder.Owner = null;
That should result in your desired JSON output.

Related

Remove itens from JSON string based on a comparison between two JSON strings - C#

Given 2 JSON strings:
[
{
"id":"BA",
"description":"BrandA",
"values":[
{
"id":"CategoryA",
"description":"CategoryA"
},
{
"id":"CategoryB",
"description":"CategoryB"
},
{
"id":"CategoryC",
"description":"CategoryC"
},
{
"id":"CategoryD",
"description":"CategoryD"
},
{
"id":"CategoryE",
"description":"CategoryE"
},
{
"id":"CategoryF",
"description":"CategoryF"
},
{
"id":"CategoryG",
"description":"CategoryG"
},
{
"id":"CategoryH",
"description":"CategoryH"
}
]
},
{
"id":"BB",
"description":"BrandB",
"values":[
{
"id":"CategoryA",
"description":"CategoryA"
},
{
"id":"CategoryB",
"description":"CategoryB"
},
{
"id":"CategoryC",
"description":"CategoryC"
}
]
}
]
AND
[
{
"id":"BA",
"description":"BrandA",
"values":[
{
"id":"CategoryA",
"description":"CategoryA"
},
{
"id":"CategoryC",
"description":"CategoryC"
}
]
},
{
"id":"BB",
"description":"BrandB",
"values":[
{
"id":"CategoryB",
"description":"CategoryB"
}
]
}
]
First one is the original. The second are the values that I want to remove from the original. So basically, if there is a match on brand and category between first and second JSON, regardless of the order of the elements, I want that match to be removed.
The expected result would be someting like this:
[
{
"id":"BA",
"description":"BrandA",
"values":[
{
"id":"CategoryB",
"description":"CategoryB"
},
{
"id":"CategoryD",
"description":"CategoryD"
},
{
"id":"CategoryE",
"description":"CategoryE"
},
{
"id":"CategoryF",
"description":"CategoryF"
},
{
"id":"CategoryG",
"description":"CategoryG"
},
{
"id":"CategoryH",
"description":"CategoryH"
}
]
},
{
"id":"BB",
"description":"BrandB",
"values":[
{
"id":"CategoryA",
"description":"CategoryA"
},
{
"id":"CategoryC",
"description":"CategoryC"
}
]
}
]
Catagory A and C in Brand A were removed as well as Category B in Brand B.
Based in some research, I was using https://github.com/wbish/jsondiffpatch.net, tried to work with it's functions, but so far I didn't manage to achieve the result I want. Also, to solve this by processing JSON direcly is not a must. If there is a simpler solution to achieve that by converting them to lists and use something like LINQ for example, it works for me as well (tried that, but didn't manage to find a way to do this comparison).
Thanks in advance.
If performance does not matter, the JsonSerializer and LINQ can be used:
Model
public class JsonModel
{
public class ValueModel
{
public string Id { get; set; }
public string Description { get; set; }
}
public string Id { get; set; }
public string Description { get; set; }
public IEnumerable<ValueModel> Values { get; set; }
}
Deserialize and LINQ
string json1Str = #"[...]";
string json2Str = #"[...]";
var opt = new System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = true };
var json1 = System.Text.Json.JsonSerializer.Deserialize<List<JsonModel>>(json1Str, opt);
var json2 = System.Text.Json.JsonSerializer.Deserialize<List<JsonModel>>(json2Str, opt);
var result = json1.
Join(json2, j1 => j1.Id, j2 => j2.Id, (j1, j2) => new JsonModel
{
Id = j1.Id,
Description = j1.Description,
Values = j1.Values.Where(j1 => !j2.Values.Select(val => val.Id).Contains(j1.Id))
});
Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(result, new System.Text.Json.JsonSerializerOptions { WriteIndented = true }));
}
Result
[
{
"Id": "BA",
"Description": "BrandA",
"Values": [
{
"Id": "CategoryB",
"Description": "CategoryB"
},
{
"Id": "CategoryD",
"Description": "CategoryD"
},
{
"Id": "CategoryE",
"Description": "CategoryE"
},
{
"Id": "CategoryF",
"Description": "CategoryF"
},
{
"Id": "CategoryG",
"Description": "CategoryG"
},
{
"Id": "CategoryH",
"Description": "CategoryH"
}
]
},
{
"Id": "BB",
"Description": "BrandB",
"Values": [
{
"Id": "CategoryA",
"Description": "CategoryA"
},
{
"Id": "CategoryC",
"Description": "CategoryC"
}
]
}
]

Restructuring a JSON object in C#

I have some JSON data that looks like this:
[
{
"name": "foo",
"id": 1,
"child": {
name: "A",
id: 1,
}
},
{
"name": "bar",
"id": 1,
"child": {
name: "A",
id: 1,
}
},
{
"name": "baz",
"id": 2,
"child": {
name: "B",
id: 2,
}
},
{
"name": "alpha",
"id": 1,
"child": {
name: "A",
id: 1,
}
}
]
I'm loading it into my .NET project and trying to restructure it so that it returns a response that looks something like this:
[
{
"name": "A"
"id": 1,
"parents": [
{
"name": "foo",
"id": 1,
},
{
"name": "bar",
"id": 1,
},
{
"name": "alpha",
"id": 1,
}
]
},
{
"name": "B"
"id": 2,
"parents": [
{
"name": "baz",
"id": 2,
}
]
}
]
I was able to use LINQ to grab all the IDs that matched, but only for 1 ID.
var test = deserializedJsonData.Where(w => w.child.id == SomeID).Select(s => s).ToList()
The moment I tried to iterate "SomeID" inside of a loop to iterate it, it freezes and crashes.
Is there a more efficient way of doing this?
EDIT: Corrected typo as pointed out in comments
Regarding your requirement, I've created the objects.
public class Base
{
public int id { get; set; }
public string name { get; set; }
}
public class Data : Base
{
public Base child { get; set; }
}
public class RequiredData : Base
{
public List<Base> parents { get; set; }
}
Then created a comparer to distinct your child objects.
public class BaseComparer : IEqualityComparer<Base>
{
public bool Equals(Base x, Base y)
{
if (x.id.Equals(y.id) && (x.name.Equals(y.name)))
{
return true;
}
else
{
return false;
}
}
public int GetHashCode(Base obj)
{
unchecked
{
int hash = 100;
hash = hash * 10 + obj.id.GetHashCode();
hash = hash * 10 + obj.name.GetHashCode();
return hash;
}
}
}
Finally,
string rawData = File.ReadAllText(#"your_json_file_path");
var parsedData = JsonConvert.DeserializeObject<Data[]>(rawData);
var childs = parsedData.GroupBy(g => g.child.id).Select(s => new Base()
{
id = s.Key,
name = s.Where(w => w.child.id == s.Key).First().child.name
}).ToList();
var result = parsedData.GroupBy(s => s.child).Select(s =>
new RequiredData()
{
id = s.Key.id,
name = s.Key.name,
parents = parsedData.Where(w => w.child.id == s.Key.id).Select(x =>
new Base()
{
id = x.id,
name = x.name
}).ToList()
}).Distinct(new BaseComparer()).ToList();
var jsonResult = JsonConvert.SerializeObject(result);
jsonResult will contain your required output.
Note: I'm using Newtonsoft.Json library for JSON operations.

Collection Navigation Properties returning odd JSON hierarchy

I have 2 classes:
public class A
{
public int Id { get; set; }
public string Name { get; set; }
public B myB { get; set; }
}
public class B
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<A> myAs { get; set; }
}
Im using Postman to test the Api calls.
public IEnumerable<B> GetBs()
{
return _context.Bs.Include(b => b.myAs).ToList();
}
returns as expected, B objects and a list of their associated A objects:
{
"Id": 1,
"Name": "B2",
"myAs": [
{
"Id": 1,
"Name": "A1"
},
{
"Id": 2,
"Name": "A2"
}
]
},
{
"Id": 2,
"Name": "B3",
"myAs": [
{
"Id": 3,
"Name": "A3"
},
{
"Id": 4,
"Name": "A4"
},
{
"Id": 5,
"Name": "A5"
}
]
}
The reverse however returns a wierd hierarchical structure:
public IEnumerable<A> GetAs()
{
return _context.As.Include(a => a.myB).ToList();
}
returns:
[
{
"Id": 1,
"Name": "A1",
"myB": {
"Id": 1,
"Name": "B2",
"myAs": [
{
"Id": 2,
"Name": "A2"
}
]
}
},
{
"Id": 2,
"Name": "A2",
"myB": {
"Id": 1,
"Name": "B2",
"myAs": [
{
"Id": 1,
"Name": "A1"
}
]
}
},
{
"Id": 3,
"Name": "A3",
"myB": {
"Id": 2,
"Name": "B3",
"myAs": [
{
"Id": 4,
"Name": "A4"
},
{
"Id": 5,
"Name": "A5"
}
]
}
}
]
The GetAs method returns A objects with B objects with further nested A objects.
My understanding after a little research (I could be very wrong here), is that because A has a navigation property to B (myB) and B has a navigation property to a list of A objects (myAs) this is causing some kind of loop.
My questions are
Is my understanding here correct ? Is this why the hierarchy is returning in this weird layout ?
How do i fix this ? I can take the ICollection navigation property out of the domain model but then I can no longer query As and their associated Bs ?
note A and B are not actually my domain models. I just wanted to keep the example as simple as possible.
Thanks in advance.
A few things here:
The output has the expected shape. As you suspect, the bidirectional references are being expanded by the serializer. Think of what would happen if you manually serialized each property of each object recursively. That is what happening.
To solve the immediate problem configure you default serializer settings like this:
jsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.Serialization.ReferenceLoopHandling.Ignore;
The above is useful when prototyping but when your application is more formalized, you should create and return dedicated view model types from your web API endpoints.
public class AViewModel
{
public int Id { get; set; }
public string Name { get; set; }
}
public class BViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public IEnumerable<AViewModel> myAs { get; set; }
}
public IEnumerable<BViewModel> GetBs()
{
return _context.Bs.Include(b => b.myAs)
.Select(b => new BViewModel
{
Id = b.Id,
Name = b.Name,
As = b.As.Select(a => new AViewModel
{
Id = a.Id,
Name = a.Name
})
})
.ToList();
}
It is worth noting that there are libraries, such as the well regarded AutoMapper, that can perform these translations between model types for you, automatically assigning corresponding properties by name using reflection.
Personally, I try to avoid reflection based approaches as much as possible as they tend to render code difficult to reason about statically. This hinders both human readers such as ourselves and tools like the C# language.
That said, it can be worth the tradeoff depending on the task at hand. I hope to eventually see language level support that eliminates such boilerplate assignments without stepping into the realm of stringiness but I have a long time to wait.

How can I use regex to extract child node from JSON structure?

Hi I try to get json data inside of the json. But my class is Employee my service creates json as com.myteam.rbffiyatlama2.Employee this prefix can be changeable so I have to write a solution to get an exact part of the json Like below but my below code is not working. I will send my node name to a method Getjsonobject(Employee emp) or Getjsonobject(Customer cust) or Getjsonobject(Student student) etc.
My Json:
{
"type": "SUCCESS",
"msg": "Container RBFFiyatlama2_1.0.1 successfully called.",
"result": {"execution-results": {
"results": [
{
"value": 2,
"key": ""
},
{
"value": {"com.myteam.rbffiyatlama2.Employee": {
"salary": 2400,
"age": 35,
"cofactor": 0.2
}},
"key": "t1"
},
{
"value": {"com.myteam.rbffiyatlama2.Employee": {
"salary": 4800,
"age": 35,
"cofactor": 0.2
}},
"key": "t2"
}
],
"facts": [
{
"value": {"org.drools.core.common.DefaultFactHandle": {"external-form": "0:50:1980606587:1980606587:100:DEFAULT:NON_TRAIT:com.myteam.rbffiyatlama2.Employee"}},
"key": "t1"
},
{
"value": {"org.drools.core.common.DefaultFactHandle": {"external-form": "0:51:2052360932:2052360932:99:DEFAULT:NON_TRAIT:com.myteam.rbffiyatlama2.Employee"}},
"key": "t2"
}
]
}}
}
class Program
{
static void Main(string[] args)
{
var employee1 = new Employee() { age = 35, cofactor = 0.2, salary = 2000 };
var employee2 = new Employee() { age = 35, cofactor = 0.2, salary = 4000 };
var list = new List<Employee>();
list.Add(employee1);
list.Add(employee2);
var uri = new Uri("http://localhost:8080/kie-server/services/rest/server/containers/instances/RBFFiyatlama2_1.0.1");
var kieclient = new KieRequestWrapper<Employee>(uri, "kieserver", "#test2018", MethodType.POST, "application/json").Add(list).Run();
Console.Write(kieclient.Content);
var match = Regex.Match(kieclient.Content, #"(?*.Employee{*})");
var result= MyParser.Parse(match, typeof(Employee)); //Desired
Console.Read();
}
}
public class Employee
{
public int age { get; set; }
public double cofactor { get; set; }
public int salary { get; set; }
}
You don't want to use XPath to get the data you need, you want to deserialize the the JSON string into an object and then get the data you need. There are many JSON serialization libraries out there, the most common one, AFAIK, is JSON.NET. You can look at how deserialization works here: https://www.newtonsoft.com/json/help/html/DeserializeObject.htm
Example:
public class Account
{
public string Email { get; set; }
public bool Active { get; set; }
public DateTime CreatedDate { get; set; }
public IList<string> Roles { get; set; }
}
string json = #"{
'Email': 'james#example.com',
'Active': true,
'CreatedDate': '2013-01-20T00:00:00Z',
'Roles': [
'User',
'Admin'
]
}";
Account account = JsonConvert.DeserializeObject<Account>(json);
Console.WriteLine(account.Email);
// james#example.com

Google Play Developer Api and JsonConvert.DeserializeObject

I am trying to deserialize an AchievementConfigurations: list REST Response from here https://developers.google.com/games/services/publishing/api/achievementConfigurations/list.
The problem is that this line only fills out the top level object and the List remains empty. No error messages are throw which makes this difficult to track down what is going on. I used a website to generate the json structured classes and after that I removed the duplicates which where unnecessary.
The response looks like this, I have removed the achievementConfiguration resource because it is really long but it can be found here https://developers.google.com/games/services/publishing/api/achievementConfigurations#resource
{
"kind": "gamesConfiguration#achievementConfigurationListResponse",
"nextPageToken": string,
"items": [
achievementConfigurations Resource
]
}
I have a series of classes I have created mirroring the data starting with the AchievementConfigurationListResponse class
public class AchievementConfigurationListResponse
{
public string kind = "gamesConfiguration#achievementConfigurationListResponse";
public string nextPageToken = "";
List<AchievementConfigurationResource> items = new List<AchievementConfigurationResource>();
}
Next up is the AchievementConfigurationResource which is an item in the list, it has several nested objects
public class AchievementConfigurationResource
{
public static string[] types = new string[] { "STANDARD", "INCREMENTAL" };
public static string[] states = new string[] { "REVEALED", "HIDDEN", "UNLOCKED" };
public string kind = "gamesConfiguration#achievementConfiguration";
public string token = "";
public string id = "";
public string achievementType = types[0];
public string initialState = states[0];
public int? stepsToUnlock;
public AchievementConfigurationDetail draft = new AchievementDataResource();
public AchievementConfigurationDetail published = new AchievementDataResource();
}
Those nested object are of this type of AchievementConfigurationDetail
public class AchievementConfigurationDetail
{
public string kind = "gamesConfiguration#achievementConfigurationDetail";
public LocalizedStringBundle name = new LocalizedStringBundle();
public LocalizedStringBundle description = new LocalizedStringBundle();
public int pointValue = 5;
public string iconUrl = "";
public int sortRank = 1;
}
Which contains several LocalizedStringBundles
public class LocalizedStringBundle
{
public string kind = "gamesConfiguration#localizedStringBundle";
public List<Translation> translations = new List<Translation>();
public class Translation
{
public string kind = "gamesConfiguration#localizedString";
public string locale = "en-US";
public string value = "";
}
}
I call this on the json with the following line:
AchievementConfigurationListResponse res = JsonConvert.DeserializeObject<AchievementConfigurationListResponse>(content);
Here is a copy of the response, sensitive data removed but the keys and structure are all intact. This one only contains a single record because the full file is something like 5000 lines long.
{
"kind": "gamesConfiguration#achievementConfigurationListResponse",
"items": [
{
"kind": "gamesConfiguration#achievementConfiguration",
"token": "Unique Token",
"id": "Unique ID",
"achievementType": "STANDARD",
"initialState": "REVEALED",
"draft": {
"kind": "gamesConfiguration#achievementConfigurationDetail",
"name": {
"kind": "gamesConfiguration#localizedStringBundle",
"translations": [
{
"kind": "gamesConfiguration#localizedString",
"locale": "en-US",
"value": "Name"
}
]
},
"description": {
"kind": "gamesConfiguration#localizedStringBundle",
"translations": [
{
"kind": "gamesConfiguration#localizedString",
"locale": "en-US",
"value": "Description"
}
]
},
"pointValue": 5,
"iconUrl": "Icon url",
"sortRank": 1
},
"published": {
"kind": "gamesConfiguration#achievementConfigurationDetail",
"name": {
"kind": "gamesConfiguration#localizedStringBundle",
"translations": [
{
"kind": "gamesConfiguration#localizedString",
"locale": "en-US",
"value": "Name"
}
]
},
"description": {
"kind": "gamesConfiguration#localizedStringBundle",
"translations": [
{
"kind": "gamesConfiguration#localizedString",
"locale": "en-US",
"value": "Description"
}
]
},
"pointValue": 5,
"iconUrl": "Icon url",
"sortRank": 1
}
}
]
}
Json.NET does not serialize private members by default. Thus you need to make AchievementConfigurationListResponse.items be public:
public List<AchievementConfigurationResource> items = new List<AchievementConfigurationResource>();
Alternatively, mark it with [JsonProperty] which enables serialization of private members:
[JsonProperty]
List<AchievementConfigurationResource> items = new List<AchievementConfigurationResource>();

Categories

Resources