Deserialization complex Json (class) - c#

I have a Json File like this.
{
"_request": {
"api_ver": 1,
"route": "/api/v2/u/SunDwarf-21353/stats/general"
},
"average_stats": {
"damage_done_avg": 3987.0,
"deaths_avg": 5.68,
"defensive_assists_avg": 0.0,
"eliminations_avg": 10.47,
"final_blows_avg": 6.12,
"healing_done_avg": 589.0,
"melee_final_blows_avg": 0.03,
"objective_kills_avg": 3.06,
"objective_time_avg": 0.007222222222222223,
"offensive_assists_avg": 0.0,
"solo_kills_avg": 2.3,
"time_spent_on_fire_avg": 0.008055555555555555
},
"battletag": "SunDwarf-21353",
"game_stats": {
"cards": 36.0,
"damage_done": 478462.0,
"damage_done_most_in_game": 13303.0,
"deaths": 682.0,
"defensive_assists": 39.0,
"defensive_assists_most_in_game": 11.0,
"eliminations": 1257.0,
"eliminations_most_in_game": 26.0,
"environmental_deaths": 12.0,
"environmental_kills": 8.0,
"final_blows": 735.0,
"final_blows_most_in_game": 16.0,
"games_played": 120.0,
"games_won": 59.0,
"healing_done": 70670.0,
"healing_done_most_in_game": 7832.0,
"kpd": 1.84,
"medals": 304.0,
"medals_bronze": 102.0,
"medals_gold": 100.0,
"medals_silver": 102.0,
"melee_final_blows": 4.0,
"melee_final_blows_most_in_game": 2.0,
"multikill_best": 3.0,
"multikills": 5.0,
"objective_kills": 368.0,
"objective_kills_most_in_game": 10.0,
"objective_time": 0.8880555555555555,
"objective_time_most_in_game": 0.026944444444444444,
"offensive_assists": 13.0,
"offensive_assists_most_in_game": 7.0,
"recon_assists": 9.0,
"solo_kills": 277.0,
"solo_kills_most_in_game": 16.0,
"time_played": 15.0,
"time_spent_on_fire": 0.9961111111111111,
"time_spent_on_fire_most_in_game": 0.08833333333333333
},
"overall_stats": {
"avatar": "https://blzgdapipro-a.akamaihd.net/game/unlocks/0x02500000000008E8.png",
"comprank": null,
"games": 120,
"level": 24,
"losses": 61,
"prestige": 0,
"win_rate": 49,
"wins": 59
},
"region": "eu"
}
So I want to Deserialie this in C# . so I create with json2csharp.com the Classes.
Now its possible to instantiate all these classes, but this is bad I don't need a instance of GameStats, or average_stats.
What's my line here, how I can make this class not creatable?
sorry for my english, hope you can follow my problem :D
best regards. alex

Simply remove unwanted properties form RootObject
public class RootObject
{
public Request _request { get; set; }
//public AverageStats average_stats { get; set; }
public string battletag { get; set; }
//public GameStats game_stats { get; set; }
public OverallStats overall_stats { get; set; }
public string region { get; set; }
}
Deserializer omits them.

So you mean you don't know how to use a deserializer?
Take a look at this link
Simply do it like this:
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

Make your classes nested to stop classes being shown in intellisense and add parameterless private constructors. Don't worry about private constructors, Json.NET uses reflection to instantiate your classes.
public class RootObject
{
public class GameStats
{
private GameStats() { }
//Code omitted
}
public class Request
{
private Request() { }
//Code omitted
}
public class AverageStats
{
private AverageStats() { }
//Code omitted
}
public class OverallStats
{
private OverallStats() { }
//Code omitted
}
public Request _request { get; set; }
public AverageStats average_stats { get; set; }
public string battletag { get; set; }
public GameStats game_stats { get; set; }
public OverallStats overall_stats { get; set; }
public string region { get; set; }
}
Compiler gives error:
//'RootObject.GameStats.GameStats()' is inaccessible due to its protection level
var myClass = new RootObject.GameStats();

Related

expected END_OBJECT but found FIELD_NAME

I'm using PostAsJsonAsync in the HttpClient to query Elastic, and it's failing on line 12, "wildcard"
I used https://json2csharp.com/ to convert the example JSON to C# objects.
This is the json that's being produced by Newtonsoft which fails.
{
"query": {
"bool": {
"must": [
{
"range": {
"#timestamp": {
"gte": "now-7d",
"lt": "now"
}
},
"wildcard": {
"request.keyword": {
"value": "/message/*/*-message/2c35669dd87e471faad1f90374d8d380/status",
"case_insensitive": true
}
}
}
]
}
}
}
This is an example that I was provided and used to convert the json to C# objects.
{
"query": {
"bool": {
"must": [
{
"range": {
"#timestamp": {
"gte": "now-7d",
"lt": "now"
}
}
},
{
"wildcard": {
"request.keyword": {
"value": "/message/*/*-message/2c35669dd87e471faad1f90374d8d380/status",
"case_insensitive": true
}
}
}
]
}
}
}
Both are valid JSON, but only the 2nd one is accepted by Elastic. It's seems to be expecting curly braces around the properties in must, but I can't figure out how to get the JSON to serialize this way.
You are encountering a limitation with code-generation tools such as https://json2csharp.com/, namely that they do not handle implied polymorphism very well. In such cases you may need to manually fix the generated classes.
Consider the following JSON array containing two different types of object:
[{"A" : "a value"},{"B" : "b value"}]
The array contains objects that either have a property A or a property B, but if you generate classes from this JSON, you will get a single, merged type with both properties:
public class Root
{
public string A { get; set; }
public string B { get; set; }
}
Whereas what you really want is something like:
public interface IRootBase { }
public class A : IRootBase
{
public string A { get; set; }
}
public class B : IRootBase
{
public string B { get; set; }
}
Given such a model, you will be able to construct a List<IRootBase> and serialize it to get the JSON shown. (And, to deserialize, see Deserializing polymorphic json classes without type information using json.net.)
In your case, the problem is with the array value of "must". As you can see this array contains two different types of object:
[
{
"range":{
"#timestamp":{
"gte":"now-7d",
"lt":"now"
}
}
},
{
"wildcard":{
"request.keyword":{
"value":"/message/*/*-message/2c35669dd87e471faad1f90374d8d380/status",
"case_insensitive":true
}
}
}
]
But https://json2csharp.com/ will create the following combined type:
public class Must
{
public Range range { get; set; }
public Wildcard wildcard { get; set; }
}
If you were to create an array with a single instance of Must containing both properties, you would get the invalid JSON rejected by Elastic.
Instead, you need to manually modify the auto generated types as follows:
#region Manually created from Must
public interface IMustConstraint { }
public class RangeConstraint : IMustConstraint
{
public Range range { get; set; }
}
public class WildcardConstraint : IMustConstraint
{
public Wildcard wildcard { get; set; }
}
#endregion Manually created from Must
public class Range
{
[JsonProperty("#timestamp")]
public Timestamp Timestamp { get; set; }
}
public class Timestamp
{
public string gte { get; set; }
public string lt { get; set; }
}
public class Wildcard
{
[JsonProperty("request.keyword")]
public RequestKeyword RequestKeyword { get; set; }
}
public class RequestKeyword
{
public string value { get; set; }
public bool case_insensitive { get; set; }
}
public class BoolQuery // Renamed from Bool for clarity
{
public List<IMustConstraint> must { get; set; } // Modified from List<Must>
}
public class Query
{
public BoolQuery #bool { get; set; }
}
public class Root
{
public Query query { get; set; }
}
And now you will be able to do:
Root root = new ()
{
query = new ()
{
#bool = new ()
{
must = new ()
{
new RangeConstraint() { range = new () { Timestamp = new () { gte = "now-7d", lt = "now" } } },
new WildcardConstraint() { wildcard = new () { RequestKeyword = new () { value = "/message/*/*-message/2c35669dd87e471faad1f90374d8d380/status", case_insensitive = true } } },
},
},
},
};
var json = JsonConvert.SerializeObject(root, Formatting.Indented);
And create your required JSON.
Demo fiddle here.

Dotnet using graphQL client, Data response not mapped

I'm using the last GraphQL client NuGet package (3.2.1) on .NET Core 3.1 project and calling a GraphQL API.
When I do the "SendQueryAsync()" or "SendMutationAsync()" the response status code is OK but the Data property is always Null.
I think it's related to the serialization but idk where is the problem.
How I use it
var graphQLClient = new GraphQLHttpClient(new GraphQLHttpClientOptions { EndPoint = new Uri(_graphQLEndPoint) }, new NewtonsoftJsonSerializer(), httpclient);
var request = new GraphQLRequest
{
Query = #"query CurrentUserCards {
currentUser {
cardsCount
cards {
name
pictureUrl
position
player {
displayName
}
}
}
}"
};
var data = await graphQLClient.SendQueryAsync<Data>(request);
Even if I put "Rootobject" class it's null.
My model
I generated my model with "Paste JSON as classes" feature on Visual studio, from the JSON result.
public class Rootobject
{
public Data data { get; set; }
}
public class Data
{
public Currentuser currentUser { get; set; }
}
public class Currentuser
{
public int cardsCount { get; set; }
public Card[] cards { get; set; }
}
public class Card
{
public string name { get; set; }
public string pictureUrl { get; set; }
public string position { get; set; }
public Player player { get; set; }
}
public class Player
{
public string displayName { get; set; }
}
Response from Postman
{
"data": {
"currentUser": {
"cardsCount": 12,
"cards": [
{
"name": "Henry",
"pictureUrl": "",
"position": "Coach",
"player": {
"displayName": "Thierry Henry",
}
},
{
"name": "Zidane",
"pictureUrl": "",
"position": "Coach",
"player": {
"displayName": "Zinedine Zidane",
}
}
...
]
}
}
}
I have solved this by removing the Rootobject class and use the Data class as root. I think that the response always has a data property so it skips that in the deserialization.

Create correct JSON class

I created a class for a json object (not automatically).
However, I don't quite understand how to make the button object contain nameless arrays as in the example?
Example JSON (what the server expects to receive):
{
"one_time":false,
"buttons":[
[
{
"action":{
"type":"location",
"payload":"{\"button\": \"1\"}"
}
}
],
[
{
"action":{
"type":"open_app",
"app_id":6232540,
"owner_id":-157525928,
"hash":"123",
"label":"LiveWidget"
}
}
],
[
{
"action":{
"type":"vkpay",
"hash":"action=transfer-to-group&group_id=181108510&aid=10"
}
}
],
[
{
"action":{
"type":"text",
"payload":"{\"button\": \"1\"}",
"label":"Red"
},
"color":"negative"
},
{
"action":{
"type":"text",
"payload":"{\"button\": \"2\"}",
"label":"Green"
},
"color":"positive"
},
{
"action":{
"type":"text",
"payload":"{\"button\": \"2\"}",
"label":"Blue"
},
"color":"primary"
},
{
"action":{
"type":"text",
"payload":"{\"button\": \"2\"}",
"label":"White"
},
"color":"secondary"
}
]
]
}
Keyboard.cs (manually created class)
public class Keyboard
{
public bool one_time { get; set; }
public List<buttons> buttons { get; set; }
}
public class buttons
{
public action action { get; set; }
public string color { get; set; }
}
public class action
{
public string type { get; set; }
public string payload { get; set; }
public string label { get; set; }
}
I create an object like this:
var Keyboard_obj = new Keyboard()
{
one_time = false,
buttons = new List<buttons>()
{
new buttons()
{
action = new action()
{
type = "test1",
label = "class obj",
payload = "{\"button\": \"1\"}"
},
color = "negative"
},
new buttons()
{
action = new action()
{
type = "test2",
label = "class obj",
payload = "{\"button\": \"2\"}"
},
color = "positive"
}
}
};
How can I remake the class so that I get a JSON object like the example above?
You should use JsonSerializer
this code will give you a Json object like the example above:
var output = JsonConvert.SerializeObject(Keyboard_obj );
you can also create an object from Json:
Keyboard keyboard = JsonConvert.DeserializeObject<Keyboard>(output );
I can suggest you to try using dynamic objects
dynamic myObject = JsonConvert.DeserializeObject<dynamic>(input);
decimal Amount = Convert.ToDecimal(myObject.Amount);
string Message = myObject.Message;
If you take your JSON and use Visual studio (use paste as special option) or online tool to generate classes from the provided JSON then following classes will be generated.
Note - RootBoject.buttons Is an array or array instead of single collection.
public class Rootobject
{
public bool one_time { get; set; }
public Button[][] buttons { get; set; }
}
public class Button
{
public Action action { get; set; }
public string color { get; set; }
}
public class Action
{
public string type { get; set; }
public string payload { get; set; }
public int app_id { get; set; }
public int owner_id { get; set; }
public string hash { get; set; }
public string label { get; set; }
}
So with the provided class schema if you use JsonConvert to searialize the RootObject then you will get the required JSON.

How to Query and Enumerate complex JSON object using JSON.Net in C#

How can I query (to see if a property exists) and enumerate (the array property) found within a complex JSON object using using JSON.NET in C# ?
I am receiving a complex JSON object from an API with a variable number/type of properties.
I keep reading the JSON.Net Documentation, reviewing samples, etc. but not gotten far and am lost in JObject, JArray, JToken, using dynamic, etc...
I want to find the pageResponses.scriptOutput property, verify it contains and .items[] array and then enumerate/iterate the array.
Edit
I made progress and found typo in JSON data example.
But how can I query/enumerate the child objects using key names, e.g.(item.location, item.timestamp) ?
string json = File.ReadAllText(#"Output.json");
JObject jObj = JObject.Parse(json);
IList<JToken> items = jObj["pageResponses"][0]["scriptOutput"]["items"].ToList();
foreach (JToken item in items){
Console.WriteLine(item["location"]);
}
/*** Console Output ***/
// Austin, TX
// Anaheim, CA
// Adams, MN
// Barstow, CA
var varItems = from o in jObj["pageResponses"][0]["scriptOutput"]["items"].ToList() select o;
foreach (var item in varItems){
Console.WriteLine(item["timestamp"]);
}
/*** Console Output ***/
// 2016 - 05 - 03 19:53
// 2016 - 05 - 04 04:10
// 2016 - 05 - 04 08:18
// 2016 - 05 - 01 12:26
(JSON sample below trimmed down for brevity)
{
"meta": {
"outputAsJson": true,
"backend": {
"os": "linux",
"id": "10.240.0.3_2",
"requestsProcessed": 8
}
},
"pageResponses": [
{
"pageRequest": {
"renderType": "script",
"outputAsJson": true
},
"frameData": {
"name": "",
"childCount": 1
},
"events": [
{
"key": "navigationRequested",
"time": "2016-05-06T13:43:30.344Z"
},
{
"key": "navigationRequested",
"time": "2016-05-06T13:43:31.131Z"
}
],
"scriptOutput": {
"items": [
{
"location": "Austin, TX",
"timestamp": "2016-05-03 19:53",
"title": "User Login"
},
{
"location": "Anaheim, CA",
"timestamp": "2016-05-04 04:10",
"title": "User Logout"
},
{
"location": "Adams, MN",
"timestamp": "2016-05-04 08:18",
"title": "User Login"
},
{
"location": "Barstow, CA",
"timestamp": "2016-05-01 12:26",
"title": "User Logout"
}
]
},
"statusCode": 200
}
],
"statusCode": 200,
"content": {
"name": "content.json",
"encoding": "utf8"
},
"originalRequest": {
"pages": [
{
"renderType": "script",
"outputAsJson": true
}
]
}
}
I suggest creating a proxy class (I used json2csharp):
public class Backend
{
public string os { get; set; }
public string id { get; set; }
public int requestsProcessed { get; set; }
}
public class Meta
{
public bool outputAsJson { get; set; }
public Backend backend { get; set; }
}
public class PageRequest
{
public string renderType { get; set; }
public bool outputAsJson { get; set; }
}
public class FrameData
{
public string name { get; set; }
public int childCount { get; set; }
}
public class Event
{
public string key { get; set; }
public string time { get; set; }
}
public class ScriptOutput
{
public List<object> items { get; set; }
}
public class PageRespons
{
public PageRequest pageRequest { get; set; }
public FrameData frameData { get; set; }
public List<Event> events { get; set; }
public ScriptOutput scriptOutput { get; set; }
public int statusCode { get; set; }
}
public class Content
{
public string name { get; set; }
public string encoding { get; set; }
}
public class Page
{
public string renderType { get; set; }
public bool outputAsJson { get; set; }
}
public class OriginalRequest
{
public List<Page> pages { get; set; }
}
public class RootObject
{
public Meta meta { get; set; }
public List<PageRespons> pageResponses { get; set; }
public int statusCode { get; set; }
public Content content { get; set; }
public OriginalRequest originalRequest { get; set; }
}
Then deserialize it:
var obj = JsonConvert.DeserializeObject<RootObject>(json);
if (obj != null && obj.pageResponses != null)
{
foreach (var pageResponse in obj.pageResponses)
{
if (pageResponse.scriptOutput == null)
continue;
foreach (var item in pageResponse.scriptOutput.items)
{
Console.WriteLine(item);
}
}
}
I do this with a couple of Extension Methods and I use JsonConvert.DeserializeObject.
Code snippets below.
Usage
ExpandoObject data = JsonConvert.DeserializeObject<ExpandoObject>(jsonString);
if(data.HasProperty("propertyToCheck"))
{
object[] objects = data.Get<object[]>("propertyToCheck");
}
In the snippet above I check a property exists, then I assign it to a .Net type, in this case an object array. Though it can be any type so long as it's sane.
Extension Methods
public static bool HasProperty(this ExpandoObject value, string property)
{
bool hasProp = false;
if (((IDictionary<String, object>)value).ContainsKey(property))
{
hasProp = true;
}
return hasProp;
}
public static T Get<T>(this ExpandoObject value, string property)
{
return (T)((IDictionary<String, dynamic>)value)[property];
}
Quick, easy and to the point!

Deserialize JSON C# Json.net [duplicate]

This question already has answers here:
How can I parse a JSON string that would cause illegal C# identifiers?
(3 answers)
Closed 8 years ago.
I am trying to deserialize a Json response from an API.
The data looks like this
{
"response": {
"6112": {
"ID": 6112,
"Title": "AdditionalPhotos"
},
"5982": {
"ID": 5982,
"Title": "BikeRide"
},
"total_records": "20",
"returned_count": 10,
"returned_records": "1-10"
}
}
C# class:
public class Products
{
public class Product
{
public string Id { get; set; }
public string Title { get; set; }
}
public Product product { get; set; }
}
public class ss
{
public Dictionary<string, Products.Product> Response { get; set; }
public string total_records { get; set; }
}
Serialization code
ss res = Newtonsoft.Json.JsonConvert.DeserializeObject<ss>(jsonData());
I can get it to work without the total_records entry and below by deserializng to a Dictionary <string , Product>. But I cannot figure out how to get it to work. This is the error I get
Error converting value "20" to type 'Products+Product'. Path 'response.total_records'
I know why I get the error, but I'm unsure how I can proceed without going in and substringing from total_records down. I have no control over the API data.
Edit: you guys are fast, I was still getting to putting the classes up
First you json is not valid one, it should look like this
{
"response":{
"6112":{
"ID":"6112",
"Title":"Additional Photos",
},
"5982":{
"ID":"5982",
"Title":"Bike Ride",
},
"total_records": "20",
"returned_count": "10",
"returned_records": "1-10",
}
}
If you mean the response to contain list it should look like this
{
"response":{
"myArray": [
{
"ID":"6112",
"Title":"Additional Photos",
},
{
"ID":"5982",
"Title":"Bike Ride",
}
],
"total_records": "20",
"returned_count": "10",
"returned_records": "1-10",
}
}
So your code look like this
public class MyArray
{
public string ID { get; set; }
public string Title { get; set; }
}
public class Response
{
public List<MyArray> myArray { get; set; }
public string total_records { get; set; }
public string returned_count { get; set; }
public string returned_records { get; set; }
}
public class RootObject
{
public Response response { get; set; }
}
If you have control over API response then please refer to Mzf's answer.
If you don't have control over API then it may not be possible to do this particular deserialization on one go. You might have to loop.
Here's my take.
Update
Modified my approach:
Created a class Response which inherits from Dictionary<string, Product>, and added the metadata parts like total_records, records_count to it's public properties. And created a JsonConverter that can deserialize JObject to Response class.
The logic used for deserialization is quite simple:
Extract the metadata parts like total_records, records_count to variables.
Then remove those metadata from the JObject, so that the key values becomes homogeneous.
Now Json.net will be easily able to serialize JObject to Response object, as key values are homogenous.
Assign the metadata extracted previously to the properties of Response object
public void Deserialize()
{
var json = #"{
'response':{
'6112':{
'ID':6112,
'Title':'Additional Photos',
},
'5982':{
'ID':5982,
'Title':'Bike Ride',
},
'total_records': '20',
'returned_count': 10,
'returned_records': '1-10',
}
}";
var responseObj = Newtonsoft.Json.JsonConvert.DeserializeObject<ss>(json, new ResponseConverter());
}
public class Response : Dictionary<string, Product>
{
public int total_records { get; set; }
public int returned_count { get; set; }
public string returned_records { get; set; }
}
public class Product
{
public string Id { get; set; }
public string Title { get; set; }
}
public class ss
{
public Response Response { get; set; }
}
public class ResponseConverter : Newtonsoft.Json.JsonConverter
{
private Response CreateResponse(Newtonsoft.Json.Linq.JObject jObject)
{
//preserve metadata values into variables
int total_records = jObject["total_records"].ToObject<int>();
var returned_records = jObject["returned_records"].ToObject<string>();
var returned_count = jObject["returned_count"].ToObject<int>();
//remove the unwanted keys
jObject.Remove("total_records");
jObject.Remove("returned_records");
jObject.Remove("returned_count");
//once, the metadata keys are removed, json.net will be able to deserialize without problem
var response = jObject.ToObject<Response>();
//Assign back the metadata to response object
response.total_records = total_records;
response.returned_count = returned_count;
response.returned_records = returned_records;
//.. now person can be accessed like response['6112'], and
// metadata can be accessed like response.total_records
return response;
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Response);
}
public override object ReadJson(Newtonsoft.Json.JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)
{
var jObject = Newtonsoft.Json.Linq.JObject.Load(reader);
Response target = CreateResponse(jObject);
serializer.Populate(jObject.CreateReader(), target);
return target;
}
public override void WriteJson(Newtonsoft.Json.JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
In my opinion this is how the JSON file should look like:
{
"response": {
"5982": {
"ID": 5982,
"Title": "BikeRide"
},
"6112": {
"ID": 6112,
"Title": "AdditionalPhotos"
},
"total_records": "20",
"returned_count": 10,
"returned_records": "1-10"
}
}
and this is how the class should look like
public class __invalid_type__5982
{
public int ID { get; set; }
public string Title { get; set; }
}
public class __invalid_type__6112
{
public int ID { get; set; }
public string Title { get; set; }
}
public class Response
{
public __invalid_type__5982 __invalid_name__5982 { get; set; }
public __invalid_type__6112 __invalid_name__6112 { get; set; }
public string total_records { get; set; }
public int returned_count { get; set; }
public string returned_records { get; set; }
}
public class RootObject
{
public Response response { get; set; }
}

Categories

Resources