Deserialize JSON C# Json.net [duplicate] - c#

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

Related

How to loop attributes in a JSON string post converting it into C# object?

I have a below json, I want to loop the items inside the attribute CheckingUrls.
{
"Root": {
"Urls": {
"CheckingUrls": [
{
"API Management": {
"url": ".azure-api.net",
"displayurl": "*.azure-api.net"
},
"Log Analytics": {
"url": "1.ods.opinsights.azure.com",
"displayurl": "*.ods.opinsights.azure.com"
}
}
]
}
}
}
Here are the C# class
public class Root
{
Urls Urls { get; set; }
}
public class Urls
{
public List<CheckingUrls> CheckingUrls { get; set; }
}
public class CheckingUrls
{
[JsonProperty("API Management")]
public UrlDetails APIManagement { get; set; }
[JsonProperty("Log Analytics")]
public UrlDetails LogAnalytics { get; set; }
}
public class UrlDetails
{
[JsonProperty("url")]
public string url { get; set; }
[JsonProperty("displayurl")]
public string displayurl { get; set; }
}
I am trying to convert it into c# object using the below code
var content = File.ReadAllText(jsonstring);
var settings = JsonConvert.DeserializeObject<Root>(content);
I am getting APIManagement and LogAnalytics as properties in the result. Is it possible to get these as List, so that I can loop the contents without hardcoding the properties in code.
Why I need solution: We might add new child to CheckingUrls and we do not want to change the c# code everytime when we change JSON.
Use a C# Dictionary when you want to convert a JSON Object to C# when you don't have a concrete type. CheckingUrls is already an array, so you end up with
public List<Dictionary<string, UrlDetails>> CheckingUrls { get; set; }
The key of a Dictionary entry is the property name in the array element (like "API Management"), and the value is the object that contains the url and displayurl properties.
This eliminates the need for the CheckingUrls C# class.
If you want a List, you can create a CheckingUrls class
List<CheckingUrls> checkingUrlsList = JObject.Parse(json)
.SelectToken("Root.Urls.CheckingUrls")
.SelectMany(jo => ((JObject)jo).Properties()
.Select(p => new CheckingUrls
{
UrlName = p.Name,
UrlDetails = new UrlDetails
{
url = (string)p.Value["url"],
displayurl = (string)p.Value["displayurl"]
}
}
)).ToList();
public class CheckingUrls
{
public string UrlName { get; set; }
public UrlDetails UrlDetails { get; set; }
}
public class UrlDetails
{
public string url { get; set; }
public string displayurl { get; set; }
}
output ( in a json format)
[
{
"UrlName": "API Management",
"UrlDetails": {
"url": ".azure-api.net",
"displayurl": "*.azure-api.net"
}
},
{
"UrlName": "Log Analytics",
"UrlDetails": {
"url": "1.ods.opinsights.azure.com",
"displayurl": "*.ods.opinsights.azure.com"
}
}
]
but if you changed your mind to a Dictionary
Dictionary<string, UrlDetails> checkingUrlsDict = JObject.Parse(json)
.SelectToken("Root.Urls.CheckingUrls")
.Select(jo => jo.ToObject<Dictionary<string, UrlDetails>>())
.FirstOrDefault();

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.

Retrieving an element from JSON response

I am trying to get a field from the response from the rest services in the Web API we are building. The JSON looks like this:
{
"d": {
"results": [{
"__metadata": {
"id": "Web/Lists(guid'4ddc-41e2-bb44-0f92ad2c0b07')/Items(164)",
"uri": "https://teams.ax.org/sites/js/project/_api/Web/Lists(guid'4ddc-41e2-bb44-0f92ad2c0b07')/Items(164)",
"etag": "\"6\"",
"type": "SP.Data.St_x0020_CdsListItem"
},
"Folder": {
"__deferred": {
"uri": "https://teams.ax.org/sites/js/project/_api/Web/Lists(guid'4ddc-41e2-bb44-0f92ad2c0b07')/Items(164)/Folder"
}
},
"ParentList": {
"__deferred": {
"uri": "https://teams.ax.org/sites/js/project/_api/Web/Lists(guid'4ddc-41e2-bb44-0f92ad2c0b07')/Items(164)/ParentList"
}
},
"PM_x0020_NameId": 220,
"St_x0020_Name": "<div class=\"ExternalClassA14DB0FF86994403B827D91158CF34B0\">KO</div>",
}]
}}
I created these model classes:
public class SharepointDTO
{
public class Metadata
{
[JsonProperty("id")]
public string id { get; set; }
[JsonProperty("uri")]
public string uri { get; set; }
[JsonProperty("etag")]
public string etag { get; set; }
[JsonProperty("type")]
public string type { get; set; }
}
public class Deferred
{
[JsonProperty("uri")]
public string uri { get; set; }
}
public class Folder
{
[JsonProperty("__deferred")]
public Deferred __deferred { get; set; }
}
public class ParentList
{
[JsonProperty("__deferred")]
public Deferred __deferred { get; set; }
}
public class Result
{
[JsonProperty("__metadata")]
public Metadata __metadata { get; set; }
[JsonProperty("Folder")]
public Folder Folder { get; set; }
[JsonProperty("ParentList")]
public ParentList ParentList { get; set; }
[JsonProperty("PM_x0020_NameId")]
public int PM_x0020_NameId { get; set; }
[JsonProperty("St_x0020_Name")]
public string St_x0020_Name { get; set; }
}
public class D
{
[JsonProperty("results")]
public IList<Result> results { get; set; }
}
public class RootObject
{
[JsonProperty("d")]
public D d { get; set; }
}
}
No trying to call the rest service from the Web API and need to get the St_x0020_Name from response and store in a string.
SharepointDTO.RootObject retSharepointobj = await GetfromSharepoint(StNumber);
string StName = retSharepointobj.d.results.St_x0020_Name.ToString();
I am deserializing the JSON in the GetfromSharepoint method like
using (var client_sharePoint = new HttpClient(handler))
{
var response = client_sharePoint.GetAsync(SP_URL).Result;
var responsedata = await response.Content.ReadAsStringAsync();
var returnObj = JsonConvert.DeserializeObject<SharepointDTO.RootObject>(responsedata);
return returnObj;
}
But it throws an error:
'System.Collections.Generic.IList' does not contain a definition for 'St_x0020_Name' and no extension method 'St_x0020_Name' accepting a first argument of type 'System.Collections.Generic.IList' could be found (are you missing a using directive or an assembly reference?)
results is an array, so you have either loop thru like
foreach(var item in retSharepointobj.d.results){
string StName = item.St_x0020_Name.ToString();
}
or get a specific element, for example a first element like:
SharepointDTO.RootObject retSharepointobj = await GetfromSharepoint(StNumber);
string StName = retSharepointobj.d.results[0].St_x0020_Name.ToString();
or you can add an extra check like that
SharepointDTO.RootObject retSharepointobj = await GetfromSharepoint(StNumber);
if(retSharepointobj.d.results.length > 0){
string StName = retSharepointobj.d.results[0].St_x0020_Name.ToString();
}
Shouldn't this line:
string StName = retSharepointobj.d.results.St_x0020_Name.ToString();
Be like this?
string StName = retSharepointobj.d.results.First().St_x0020_Name.ToString();

JSON .NET deserialize string[] instead of string [duplicate]

This question already has answers here:
How to handle both a single item and an array for the same property using JSON.net
(9 answers)
Closed 7 years ago.
I'm using the Yahoo fantasy sports api. I'm getting a result like this:
"player": [
{
...
"eligible_positions": {
"position": "QB"
},
...
},
{
...
"eligible_positions": {
"position": [
"WR",
"W/R/T"
]
},
...
},
How is it that I can deserialize this?
My code looks like this:
var json = new JavaScriptSerializer();
if (response != null)
{
JSONResponse JSONResponseObject = json.Deserialize<JSONResponse>(response);
return JSONResponseObject;
}
And in my JSONResponse.cs file:
public class Player
{
public string player_key { get; set; }
public string player_id { get; set; }
public string display_position { get; set; }
public SelectedPosition selected_position { get; set; }
public Eligible_Positions eligible_positions { get; set; }
public Name name { get; set; }
}
public class Eligible_Positions
{
public string position { get; set; }
}
When I run this, since eligible_positions can return both a string and a string array, I keep getting the error "Type 'System.String' is not supported for deserialization of an array".
I've also tried turning public string position { get; set; } to public string[] position { get; set; } but I still get an error.
How should I handle this?
I'll use Json.Net. The idea is: "declare position as a List<string> and if the value in json is a string. then convert it to a List"
Code to deserialize
var api = JsonConvert.DeserializeObject<SportsAPI>(json);
JsonConverter
public class StringConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException();
}
public override object ReadJson(Newtonsoft.Json.JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)
{
if(reader.ValueType==typeof(string))
{
return new List<string>() { (string)reader.Value };
}
return serializer.Deserialize<List<string>>(reader);
}
public override void WriteJson(Newtonsoft.Json.JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Sample Json
{
"player": [
{
"eligible_positions": {
"position": "QB"
}
},
{
"eligible_positions": {
"position": [
"WR",
"W/R/T"
]
}
}
]
}
Classes (Simplified version)
public class EligiblePositions
{
[JsonConverter(typeof(StringConverter))] // <-- See This
public List<string> position { get; set; }
}
public class Player
{
public EligiblePositions eligible_positions { get; set; }
}
public class SportsAPI
{
public List<Player> player { get; set; }
}

How to handle json that returns both a string and a string array? [duplicate]

This question already has answers here:
How to handle both a single item and an array for the same property using JSON.net
(9 answers)
Closed 7 years ago.
I'm using the Yahoo fantasy sports api. I'm getting a result like this:
"player": [
{
...
"eligible_positions": {
"position": "QB"
},
...
},
{
...
"eligible_positions": {
"position": [
"WR",
"W/R/T"
]
},
...
},
How is it that I can deserialize this?
My code looks like this:
var json = new JavaScriptSerializer();
if (response != null)
{
JSONResponse JSONResponseObject = json.Deserialize<JSONResponse>(response);
return JSONResponseObject;
}
And in my JSONResponse.cs file:
public class Player
{
public string player_key { get; set; }
public string player_id { get; set; }
public string display_position { get; set; }
public SelectedPosition selected_position { get; set; }
public Eligible_Positions eligible_positions { get; set; }
public Name name { get; set; }
}
public class Eligible_Positions
{
public string position { get; set; }
}
When I run this, since eligible_positions can return both a string and a string array, I keep getting the error "Type 'System.String' is not supported for deserialization of an array".
I've also tried turning public string position { get; set; } to public string[] position { get; set; } but I still get an error.
How should I handle this?
I'll use Json.Net. The idea is: "declare position as a List<string> and if the value in json is a string. then convert it to a List"
Code to deserialize
var api = JsonConvert.DeserializeObject<SportsAPI>(json);
JsonConverter
public class StringConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException();
}
public override object ReadJson(Newtonsoft.Json.JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)
{
if(reader.ValueType==typeof(string))
{
return new List<string>() { (string)reader.Value };
}
return serializer.Deserialize<List<string>>(reader);
}
public override void WriteJson(Newtonsoft.Json.JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Sample Json
{
"player": [
{
"eligible_positions": {
"position": "QB"
}
},
{
"eligible_positions": {
"position": [
"WR",
"W/R/T"
]
}
}
]
}
Classes (Simplified version)
public class EligiblePositions
{
[JsonConverter(typeof(StringConverter))] // <-- See This
public List<string> position { get; set; }
}
public class Player
{
public EligiblePositions eligible_positions { get; set; }
}
public class SportsAPI
{
public List<Player> player { get; set; }
}

Categories

Resources