C# How to deserialize this kind of JSON? - c#

I have a JSON file like below, the first field has more than 500 values, how can I deserialize to C# class at bottom?
[
{
"E1001": { "MESSAGE": "", "CODE": 1001 }
},
{
"E1002": { "MESSAGE": "", "CODE": 1002 }
}
]
public class RootModel
{
public string ErrorCode { get; set; }
public ErrorDetail Details { get; set; }
}
public class ErrorDetailModel
{
public string Message { get; set; }
public string Code { get; set; }
}

I end up using AdditionalProperties, it will map all "un-mapped" values to JsonSchema object, JsonScheme give me List<key,value>, key is the error code, and value is { "MESSAGE": "", "CODE": 1001 }.
https://www.newtonsoft.com/json/help/html/P_Newtonsoft_Json_Schema_JsonSchema_AdditionalProperties.htm

you can use something like this
List<RootModel> rootModels = JArray.Parse(json).Select(jo =>((JObject)jo).Properties().First())
.Select(prop => new RootModel {
ErrorCode = prop .Name,
Details=prop.Value.ToObject<ErrorDetail>()
}).ToList();

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);

Newtonsoft JSON Deserialize AND jsonfreeze

I have a two simple PHP class
class Order{
public $orderNo;
public $lines = array();
public $paid = false;
public function addLine(OrderLine $line) {
$this->lines[] = $line;
}
public function setPaid($paid = true) {
$this->paid = true;
}
}
class OrderLine{
public function __construct($item, $amount){
$this->item = $item;
$this->amount = $amount;
}
public $item;
public $amount;
public $options;
}
Serialize object uses https://github.com/mindplay-dk/jsonfreeze
...
$json = new JsonSerializer;
$data = $json->serialize($order);
Have output:
{
"#type": "Order",
"orderNo": 123,
"lines": [{
"#type": "OrderLine",
"item": "milk \"fuzz\"",
"amount": 3,
"options": null
},{
"#type": "OrderLine",
"item": "cookies",
"amount": 7,
"options": {
"#type": "#hash",
"flavor": "chocolate",
"weight": "1\/2 lb"
}
}],
"paid": true
}
Send the string XMLRPC in VB.NET
As using Newtonsoft JSON get a live object?
As well as how to create a compatible format by analogy with the json string of living VB.net OR C# object?
Here's something you could start with. You create some classes with properties which represent the JSON Format (untested code, just as idea):
public class MyData
{
[JsonProperty("#type")]
public string Type { get; set; }
[JsonProperty("#orderNo")]
public int OrderNo { get; set;
[JsonProperty("paid")]
public bool Paid { get; set; }
[JsonProperty("lines")]
public List<MyDataLine> Lines { get; set; }
}
public class MyDataLines
{
[JsonProperty("#type")]
public string Type { get; set; }
[JsonProperty("options")]
public MyDataLinesOptions Options { get; set; }
// ... more
}
public class MyDataLinesOptions
{
// ... more
}
Then you can serialize and deserialize the the data like this:
string json = "the json data you received";
MyData myData = JsonConvert.DeserializeObject<MyData>(json);
// ...
json = JsonConvert.SerializeObject(myData);
"#type": "Order"
and
"#type": "OrderLine",
this is not a property, this is an indication of the type of the object

Failing to Parse Json to .NET class

Given:
The classes:
public class Venue
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("location")]
public Location Location { get; set; }
}
public class Location
{
public long Lat { get; set; }
public long Lng { get; set; }
public int Distance { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Country { get; set; }
}
public class Response
{
private List<Venue> _venues;
[JsonProperty("venues")]
public List<Venue> Venues
{
get { return _venues ?? (_venues = new List<Venue>()); }
set { _venues = value; }
}
}
And a response request:
{
"meta":{
"code":200
},
"response":{
"venues":[
{
"id":"4f96a5aee4b01cb74e4dc3c6",
"name":"Centro",
"contact":{
},
"location":{
"address":"Centro",
"lat":-21.256906640441052,
"lng":-48.31978432813259,
"distance":185,
"city":"Jaboticabal",
"state":"SP",
"country":"Brazil",
"cc":"BR"
},
"canonicalUrl":"https:\/\/foursquare.com\/v\/centro\/4f96a5aee4b01cb74e4dc3c6",
"categories":[
{
"id":"4f2a25ac4b909258e854f55f",
"name":"Neighborhood",
"pluralName":"Neighborhoods",
"shortName":"Neighborhood",
"icon":{
"prefix":"https:\/\/foursquare.com\/img\/categories_v2\/parks_outdoors\/neighborhood_",
"suffix":".png"
},
"primary":true
}
],
"verified":false,
"restricted":true,
"stats":{
"checkinsCount":1106,
"usersCount":86,
"tipCount":0
},
"specials":{
"count":0,
"items":[
]
},
"hereNow":{
"count":0,
"groups":[
]
},
"referralId":"v-1376511204"
},
{
"id":"4c38b0b21a38ef3b56b39221",
"name":"Ice by Nice",
"contact":{
},
"location":{
"address":"Jaboticabal Shopping",
"lat":-21.25513775,
"lng":-48.32320093,
"distance":253,
"city":"Jaboticabal",
"state":"SP",
"country":"Brazil",
"cc":"BR"
},
"canonicalUrl":"https:\/\/foursquare.com\/v\/ice-by-nice\/4c38b0b21a38ef3b56b39221",
"categories":[
{
"id":"4bf58dd8d48988d1c9941735",
"name":"Ice Cream Shop",
"pluralName":"Ice Cream Shops",
"shortName":"Ice Cream",
"icon":{
"prefix":"https:\/\/foursquare.com\/img\/categories_v2\/food\/icecream_",
"suffix":".png"
},
"primary":true
}
],
"verified":false,
"restricted":true,
"stats":{
"checkinsCount":656,
"usersCount":309,
"tipCount":15
},
"specials":{
"count":0,
"items":[
]
},
"hereNow":{
"count":2,
"groups":[
{
"type":"others",
"name":"Other people here",
"count":2,
"items":[
]
}
]
},
"referralId":"v-1376511204"
}
]
}
}
When using JSON.NET like so:
Response response = JsonConvert.DeserializeObject<Response>(jsonString);
The deserialized response object has a empty list of venues, What am I doing wrong?
Thanks
There's a bit of a mismatch in what you're trying to deserialize and the class you have defined to deserialize into. You unfortunately need yet another layer of indirection. Note the response has;
// theres an outer object here which contains response
{
"meta":{ "code":200 },
"response":{
// you're trying to deserialize this, but it's not the entire response, it's a property of an anonymous object
}
}
So if I make a new class;
public class ResponseWrapper
{
public object meta;
public Response response;
}
And instead do;
ResponseWrapper response = JsonConvert.DeserializeObject<ResponseWrapper>(jsonString);
Then it will work.
Note that when you're deserializing using json.NET you have to define a structure that exactly matches the json. In this case you're leaving out the outer most object. It is kind of an annoyance and leads to a lot of code like what I just wrote but that's just the way it goes sometimes.

JSON.NET won't deserialize into my object, throws an exception

My JSON (myString) looks like this:
"http://d.opencalais.com/dochash-1/0701d73f-2f99-39e1-8c29-e61ee8bf3238/cat/1":
{
"_typeGroup": "topics",
"category": "http://d.opencalais.com/cat/Calais/Law_Crime",
"classifierName": "Calais",
"categoryName": "Law_Crime",
"score": 0.869
}
I am trying to deserialise the above exact string into an object:
public class OpenCalaisResult
{
public string _typeGroup {get; set; }
public string category { get; set; }
public string categoryName { get; set; }
public string classifierName { get; set; }
public decimal score { get; set; }
}
I am trying this code:
OpenCalaisResult myObject = (OpenCalaisResult)JsonConvert.DeserializeObject(myString, typeof(OpenCalaisResult), settings);
I get an exception:
{"Error converting value
\"http://d.opencalais.com/dochash-1/0701d73f-2f99-39e1-8c29-e61ee8bf3238/cat/1\"
to type 'MyApp.Parsers.JsonTypes.OpenCalaisResult'. Path
'', line 1, position 78."}
Any idea what I am doing wrong?
your json should be like this
{
"http://d.opencalais.com/dochash-1/0701d73f-2f99-39e1-8c29-e61ee8bf3238/cat/1":
{
"_typeGroup": "topics",
"category": "http://d.opencalais.com/cat/Calais/Law_Crime",
"classifierName": "Calais",
"categoryName": "Law_Crime",
"score": 0.869
}
}
http://www.json.org/
Shows objects have the form ...
{ ... }
The form of your JSON is ...
x : { ... }
I'm guessing that 'x' is the type of the class being [de]serialised ... I don't believe it's supported by your library.
JSON support is a bit random.

Parsing multi-level JSON array

I'm trying to get objects from a multi-level JSON array. This is the an example table:
array(2) {
["asd"]=>
array(3) {
["id"]=>
int(777)
["profile"]=>
array(4) {
["username"]=>
string(5) "grega"
["email"]=>
string(18) "random#example.com"
["image"]=>
string(16) "http...image.jpg"
["age"]=>
int(26)
}
["name"]=>
string(5) "Grega"
}
["be"]=>
array(4) {
["username"]=>
string(5) "grega"
["email"]=>
string(18) "random#example.com"
["image"]=>
string(16) "http...image.jpg"
["age"]=>
int(26)
}
}
The string I'm trying to reach is either of the emails (example). This is how I try it:
public class getAsd
{
public string asd;
}
public class Profile
{
public string username { get; set; }
public string email { get; set; }
public string image { get; set; }
public string age { get; set; }
}
}
And then using JavaScriptSerilization.Deserilize<Asd>(jsonData); to deserilize it, but when I try the same with "Profile", it gives me the following error:
No parameterless constructor defined for type of 'System.String'.
JSON:
{"asd":{"id":777,"profile":{"username":"grega","email":"random#example.com","image":"http...image.jpg","age":26},"name":"Grega"},"be":{"username":"grega","email":"random#example.com","image":"http...image.jpg","age":26}}
And idea what might be wrong?
[EDIT: Smarm removed. OP did add JSON in an edit.]
Your profile class, as JSON, should resemble the following.
{
"username":"grega",
"email":"random#example.com",
"image":"http...image.jpg",
"age":"26",
"roles": [
{"name": "foo"},
{"name": "bar"}
]
}
array should not show up in JSON unless its part of a property name ("codearray") or property value("There's no 'array' in JSON. There's no 'array' in JSON.").
Arrays of objects in JSON are encased in square brackets [] and comma-delimited. An array/collection of profiles in JSON:
[
{
"username":"gretta",
"email":"mrshansel#example.com",
"image":"http...image.jpg",
"age":"37",
"roles": [
{"name": "foo"},
{"name": "bar"}
]
},
{
"username":"methusaleh",
"email":"old#men.org",
"image":"http...image.jpg",
"age":"2600",
"roles": [
{"name": "foo"},
{"name": "},
{"name": "bar"}
]
},
{
"username":"goldilocks",
"email":"porridge#bearshous.com",
"image":"http...image.jpg",
"age":"11",
"roles": [
{"name": "foo"}
]
}
]
While that may not fully answer your question, could you start with that and update your question?
EDIT:
See this answer by Hexxagonal for the complete approach.
Alright, here was what a "basic" version of your classes would be. You should really follow a standard of having properties have their first letter capitalized. Since you did not do this earlier, I maintained that style.
public class Type1
{
public TypeAsd asd { get; set; }
public TypeBe be { get; set; }
}
public class TypeAsd
{
public int id { get; set; }
public TypeBe profile { get; set; }
public string name { get; set; }
}
public class TypeBe
{
public string username { get; set; }
public string email { get; set; }
public string image { get; set; }
public int age { get; set; }
}
You deserialization code would then look something like the following:
string jsonString = "{\"asd\":{\"id\":777,\"profile\":{\"username\":\"grega\",\"email\":\"random#example.com\",\"image\":\"http...image.jpg\",\"age\":26},\"name\":\"Grega\"},\"be\":{\"username\":\"grega\",\"email\":\"random#example.com\",\"image\":\"http...image.jpg\",\"age\":26}}";
JavaScriptSerializer serializer = new JavaScriptSerializer();
Type1 obj = serializer.Deserialize<Type1>(jsonString);

Categories