This question already has answers here:
How can I parse a JSON string that would cause illegal C# identifiers?
(3 answers)
Using JSON.NET to read a dynamic property name
(1 answer)
Create a strongly typed c# object from json object with ID as the name
(1 answer)
Closed 4 years ago.
I have a JSON string like this:
{
"ProfileChange": 5,
"profileId": "anId",
"profileChanges": [
{
"changeType": "fullProfileUpdate",
"profile": {
"_id": "g3fwerfgscsdadad",
"rvn": 7,
"wipeNumber": 5,
"accountId": "gw4gefsdfswfwfwdqe",
"profileId": "anId",
"version": "latestUpdate",
"items": {
"abc-123": {
"templateId": "default",
"attributes": {
"max": 0,
"bonus": 1,
"seen": 1,
"xp": 0,
"variants": [],
"favorite": false
},
"quantity": 1
},
"zxc-456": {
"templateId": "default",
"attributes": {
"max": 0,
"bonus": 1,
"seen": 1,
"xp": 0,
"variants": [],
"favorite": false
},
"quantity": 1
}
}
}
}
]
}
I want to deserialize it to a C# object, and here is my code:
class Model
{
[JsonProperty("ProfileChange")] public long ProfileChange { get; set; }
[JsonProperty("profileId")] public string ProfileId { get; set; }
[JsonProperty("profileChanges")] public List<ProfileChange> ProfileChanges { get; set; }
}
public class ProfileChange
{
[JsonProperty("changeType")] public string ChangeType { get; set; }
[JsonProperty("profile")] public Profile Profile { get; set; }
}
public class Profile
{
[JsonProperty("_id")] public string Id { get; set; }
[JsonProperty("rvn")] public long Rvn { get; set; }
[JsonProperty("wipeNumber")] public long WipeNumber { get; set; }
[JsonProperty("accountId")] public string AccountId { get; set; }
[JsonProperty("profileId")] public string ProfileId { get; set; }
[JsonProperty("version")] public string Version { get; set; }
[JsonProperty("items")] public Items Items { get; set; }
}
public class Items
{
public List<IDictionary<string, ItemDetails>> Item { get; set; }
}
public class ItemDetails
{
[JsonProperty("templateId")] public string TemplateId { get; set; }
}
And then I implement it in my main implementation like this:
var obj = JsonConvert.DeserializeObject<Model>(json);
And it does deserialize most of the data, however, the part I am struggling to deserialize is the Items one. Since they are generated dynamically, I want to create a dictionary of the items, with string for the entry and ItemDetails for the details of it.
I have used Dictionary and also made it a list, since there are multiple items, but it still is null. I also tried non-list, didn't return anything. Only null. All other details are fine, except the dictionary and dynamically generated JSON data.
I need help to fix the issue with deserializer returning null for the Items Dictionary.
Related
This question already has answers here:
Json.NET deserialize or serialize json string and map properties to different property names defined at runtime
(4 answers)
Closed last month.
I am writing a custom extension to DefaultContractResolver to map names between a JSON string and models to be deserialized.
The naming in these models is not consistent and I was specifically asked to not change them (renaming, adding attributes, etc).
The json looks like this
"parameter": {
"alarms": [
{
"id": 1,
"name": "alarm1",
"type": 5,
"min": 0,
"max": 2
}
],
"settings": [
{
"id": 1,
"name": "setting1",
"description": "something here"
"type": 1,
"value": 2
}
]
}
The Parameter class (output) has models for Alarms and Settings.
For example, these models look like this:
public class Alarm
{
public int AlarmId { get; set; }
public string AlarmName { get; set; }
public AlarmType RbcType { get; set; }
public int MinimumTolerated { get; set; }
public int MaximumTolerated { get; set; }
}
public class Setting
{
public int SettId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public SettingType Type { get; set; }
public int IntValue { get; set; }
}
As an example the value of "id" in the json can relate to AlarmId or SettId, so I cannot have just one resolver to perform the ResolvePropertyName(string propertyName)
And I don't know how to get about with this.
I don't think that you need any mapper, I would use this code
var jObj = JObject.Parse(json)["parameter"];
var parameters = new
{
alarms = jObj["alarms"].Select(x => new Alarm { AlarmId = (int)x["id"], AlarmName = (string)x["name"] }).ToList(),
settings = jObj["settings"].Select(x => new Setting { SettId = (int)x["id"] }).ToList()
};
I am retrieving some JSON from an API call and deserializing it into it's component objects. Everything works absolutely fine, until I come to saving to the database. The reason is, there are child objects with duplicate keys (which is absolutely correct as far as the data is concerned) but when I save the top level object, it throws a primary key violation error on the child object.
Here is a sample of my JSON (I know it is not complete);
{
"count": 149,
"filters": {},
"competitions": [
{
"id": 2006,
"area": {
"id": 2001,
"name": "Africa",
"countryCode": "AFR",
"ensignUrl": null
},
"name": "WC Qualification",
"code": null,
"emblemUrl": null,
"plan": "TIER_FOUR",
"currentSeason": {
"id": 555,
"startDate": "2019-09-04",
"endDate": "2021-11-16",
"currentMatchday": null,
"winner": null
},
"numberOfAvailableSeasons": 2,
"lastUpdated": "2018-06-04T23:54:04Z"
},
{
"id": 2025,
"area": {
"id": 2011,
"name": "Argentina",
"countryCode": "ARG",
"ensignUrl": null
},
"name": "Supercopa Argentina",
"code": null,
"emblemUrl": null,
"plan": "TIER_FOUR",
"currentSeason": {
"id": 430,
"startDate": "2019-04-04",
"endDate": "2019-04-04",
"currentMatchday": null,
"winner": null
},
"numberOfAvailableSeasons": 2,
"lastUpdated": "2019-05-03T05:08:18Z"
},
{
"id": 2023,
"area": {
"id": 2011,
"name": "Argentina",
"countryCode": "ARG",
"ensignUrl": null
},
"name": "Primera B Nacional",
"code": null,
"emblemUrl": null,
"plan": "TIER_FOUR",
"currentSeason": {
"id": 547,
"startDate": "2019-08-16",
"endDate": "2020-06-14",
"currentMatchday": 30,
"winner": null
},
"numberOfAvailableSeasons": 3,
"lastUpdated": "2020-05-15T00:00:02Z"
},
Currently I am just saving the top level object, and I expect/want all of the child objects to save too. If I turn off the primary keys on the child objects (make them identidy columns rather than their actual values), this all works fine and all of the child objects save perfectly. As you can see from the JSON, "area" 2011 is a duplicate, there are two competitions that have the same area, so data wise it is correct, but with the proper primary keys of the "area" turned on, it trips out as it is trying to insert a duplicate record.
So I understand perfectly what is going on and why it is erroring, what I want to know is, is there a simple way of just telling EF to ignore duplicate key errors. I can't add a try catch to saving the top level object as it just saves nothing when it hits the error.
I have tried saving the individual child objects, testing for their existence prior to the save, but when it tries to save the parent level object, it tries to save the child object too, leaving me with the same problem.
Here is my code for saving the top level object (cut down for simplicity);
public class Area
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int id { get; set; }
public string name { get; set; }
public string countryCode { get; set; }
public string ensignUrl { get; set; }
}
public class Winner
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int id { get; set; }
public string name { get; set; }
public string shortName { get; set; }
public string tla { get; set; }
public string crestUrl { get; set; }
}
public class CurrentSeason
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int id { get; set; }
public string startDate { get; set; }
public string endDate { get; set; }
public int? currentMatchday { get; set; }
public Winner winner { get; set; }
}
public class Competition
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int id { get; set; }
public Area area { get; set; }
public string name { get; set; }
public string code { get; set; }
public string emblemUrl { get; set; }
public string plan { get; set; }
public CurrentSeason currentSeason { get; set; }
public int numberOfAvailableSeasons { get; set; }
public DateTime lastUpdated { get; set; }
}
public class Example
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int count { get; set; }
public IList<Competition> competitions { get; set; }
}
static void Main(string[] args)
{
string json = GET(#"http://my.url.com/api/stuff");
Example example = JsonConvert.DeserializeObject<Example>(json);
using(var db = new ExampleContext())
{
db.Examples.Add(example);
db.SaveChanges();
}
}
Thanks in anticipation.
Unfortunately there isn't any straight way to overcome your problem.
EF Change Tracker tracks entities by their reference and the only way to solve your problem is to create same object for all identical areas.
For this you have two choices:
1- Loop over example after this line
Example example = JsonConvert.DeserializeObject<Example>(json);
and find all identical areas and replace all with one of them.
2- Use PreserveReferencesHandling feature of NewtonSoft. But it needs to apply on both Serialize and Deserialize sides:
Server(Api) side:
string json = JsonConvert.SerializeObject(data, Formatting.Indented,
new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects });
Client side:
var example = JsonConvert.DeserializeObject<Example>(json,
new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects });
I have this JSON and I cannot figure out how to convert it to a List of objects in C#.
Here is the JSON:
{
"2": {
"sell_average": 239,
"buy_average": 238,
"overall_average": 240,
"id": 2
},
"6": {
"sell_average": 184434,
"buy_average": 182151,
"overall_average": 189000,
"id": 6
},
"8": {
"sell_average": 11201,
"buy_average": 1723,
"overall_average": 180,
"id": 8
}
}
And the code I've tried using:
public class ItemSummaryModel
{
public string Id { get; set; }
public ItemSummary ItemSummary { get; set; }
}
public class ItemSummary
{
public int Sell_Average { get; set; }
public int Buy_Average { get; set; }
public int Overall_Average { get; set; }
public int Id { get; set; }
}
List<ItemSummaryModel> models =
JsonConvert.DeserializeObject<List<ItemSummaryModel>>(jsonSummary);
to no avail. How can I deserialize this JSON into lists of these objects using Newtonsoft's JSON library (Json.Net)?
You can use
var dict = JsonConvert.DeserializeObject<Dictionary<int, ItemSummary>>(json);
var items = dict.Values.ToList(); //if you want a List<ItemSummary>;
public class ItemSummaryModel
{
[JsonProperty(PropertyName = "2")]
public ItemSummary Two { get; set; }
[JsonProperty(PropertyName = "6")]
public ItemSummary Six { get; set; }
[JsonProperty(PropertyName = "8")]
public ItemSummary Eight { get; set; }
}
var result = JsonConvert.DeserializeObject<ItemSummaryModel>(json);
This will technically work to get you a complex object, but I don't much like it. Having numbers for property names is going to be an issue. This being json for a single complex object will also be tricky. If it were instead a list, you could begin writing your own ContractResolver to handle mapping the number property name to an id field and the actual object to whatever property you wanted.
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).
This question already has answers here:
Json.net failing to load certain properties belonging to a class object?
(2 answers)
Closed 4 years ago.
I am trying to deserialise some JSON which looks like this:
{
"Results":{
"Prediction":{
"type":"table",
"value":{
"ColumnNames":[
"HT",
"AT",
"X",
"Y",
"Z"
],
"ColumnTypes":[
"String",
"String",
"Double",
"Double",
"Double"
],
"Values":[
[
"Mum",
"Dad",
"0.172627246490883",
"0.171741768332677",
"0.65563098517644"
],
[
"Father",
"Mother",
"0.391368227731864",
"0.21270005247278",
"0.395931719795356"
]
]
]
}
}
}
}
The C# class looks like this:
public class RootObject
{
public Results Results { get; set; }
}
public class Results
{
public Prediction Prediction { get; set; }
}
public class Prediction
{
public string type { get; set; }
public Value value { get; set; }
}
public class Value
{
string[] ColumnNames { get; set; }
string[] ColumnTypes { get; set; }
string[][] Values { get; set; }
}
It deserialises up to the final property "value", which is not matched. If I turn on the error handling to see why I get the following error:
Additional information: Could not find member 'ColumnNames' on object of type 'Value'. Path 'Results.Prediction.value.ColumnNames', line 1, position 64.
I have a simple C# example which reproduces the whole problem:
var derek = #"{""Results"":{""Prediction"":{""type"":""table"",""value"":{""ColumnNames"":[""HT"",""AT"",""X"",""Y"",""Z""],""ColumnTypes"":[""String"",""String"",""Double"",""Double"",""Double""],""Values"":[[""Mum"",""Dad"",""0.172627246490883"",""0.171741768332677"",""0.65563098517644""],[""Father"",""Mother"",""0.391368227731864"",""0.21270005247278"",""0.395931719795356""]]]}}}}";
var returnedObj = JsonConvert.DeserializeObject <RootObject> (derek, settings);
I am pretty sure my class matches the JSON. Why doesn't it deserialise?
In C# properties default to private, they need to be public to be picked up by Newtonsoft/Json
public class Value
{
public string[] ColumnNames { get; set; }
public string[] ColumnTypes { get; set; }
public string[][] Values { get; set; }
}