Deserialize List<Object> using NewtonSoft. Cannot convert IList - c#

I am trying to parse a List of Comment objects from here. A comment object is a class in the leankit namespace: LeanKit.API.Client.Library.TransferObjects.Comment but I get an error in the final line of the block below specifically over responseString:
cannot convert from 'System.Collections.Generic.IList' to string
Why am I getting this? I am specifying a custom class I created specifically to deserialize the list:
public class MyCommentList
{
public string ReplyText { get; set; }
public List<Comment> ReplyData { get; set; }
public string ReplyCode { get; set; }
}
calling the class
var url = "https://" + acctName + ".leankit.com/kanban/api/card/getcomments/" + boardid + "/" + cardid;
var responseString = await url.WithBasicAuth("xxx", "yyy").GetJsonListAsync();
MyCommentList mycomment = JsonConvert.DeserializeObject<MyCommentList>(responseString);
a cleaner version of calling the class (using Flurl):
var url = "https://" + acctName + ".leankit.com/kanban/api/card/getcomments/" + boardid + "/" + cardid;
MyCommentList mycomment = await url.WithBasicAuth("xxx", "yyy").GetAsync().ReceiveJson<MyCommentList>();
The JSON structure (from the link above) reproduced here:
{
"ReplyData": [
[
{
"Id": 256487698,
"Text": "First comment for this card.",
"TaggedUsers": null,
"PostDate": "10/14/2015 at 04:36:02 PM",
"PostedByGravatarLink": "3ab1249be442027903e1180025340b3f",
"PostedById": 62984826,
"PostedByFullName": "David Neal",
"Editable": true
}
]
],
"ReplyCode": 200,
"ReplyText": "Card comments successfully retrieved."
}

In the JSON, "ReplyData" is a 2d jagged array:
{
"ReplyData": [ [ ... ] ],
}
In you model it is a 1d list:
public List<Comment> ReplyData { get; set; }.
You need to change it to public List<List<Comment>> ReplyData { get; set; } to reflect the actual JSON:
public class MyCommentList
{
public string ReplyText { get; set; }
public List<List<Comment>> ReplyData { get; set; }
public string ReplyCode { get; set; }
}
Where I assume Comment is taken from https://github.com/LeanKit/LeanKit.API.Client/blob/master/LeanKit.API.Client.Library/TransferObjects/Comment.cs.
If there is a chance that it is sometimes a 1d array and sometimes a 2d array, you may need to apply SingleOrArrayConverter<Comment> from this answer to How to handle both a single item and an array for the same property using JSON.net by Brian Rogers like so:
public class MyCommentList
{
public string ReplyText { get; set; }
[JsonProperty(ItemConverterType = typeof(SingleOrArrayConverter<Comment>))]
public List<List<Comment>> ReplyData { get; set; }
public string ReplyCode { get; set; }
}
Working sample .Net fiddle here.

Related

C# Json.Net - How to deserialize an array of objects containing an array of other objects

The Json data received from the http server is like this:
[
{
"token":"NlhYnzF0xqG"
},
[
{
"ts":"2019-03-21 14:06:22.123",
"id":"CC_RTC_Second",
"val":"22"
},
{
"ts":"2019-03-21 14:06:00.096",
"id":"CC_RTC_Minute",
"val":"6"
},
{
"ts":"2019-03-21 14:00:00.276",
"id":"CC_RTC_Hour",
"val":"14"
}
]
]
I have tried some techniques presented in Newtonsoft.Json documentation, but I could not find the correct way. I spent two days by testing the solutions from StackOverflow answers, but with no success.
What C# types and techniques should I use in this particular case?
Data structure can vary:
complete list of attributes is: td, id, val, flags, type, uts, nr.
All of them are strings.
Server can omit the attrs if they do not exist, so for example I can obtain only ts + id.
Is there any way how to work with such a data?
First of all your json is quite complicated and its tedious job to create a class hierarchy for your json,
But one simple approach is that if you parse your JSON to JArray and then takes
0th element to one class
And all remaining into list of another class
Then might be you can retrieve all your json data
string json = File.ReadAllText(#"Path to your json");
JArray jArray = JArray.Parse(json);
Token token = jArray[0].ToObject<Token>();
jArray.RemoveAt(0);
RandomData[] tokenData = jArray.First.ToObject<RandomData[]>();
//--------------------Print Data to Console-------------------
Console.WriteLine(token.token + "\n");
foreach (var item in tokenData)
{
Console.WriteLine("ts: " + item.ts);
Console.WriteLine("id: " + item.id);
Console.WriteLine("val: " + item.val + "\n");
}
Console.ReadLine();
And classes are,
class Token
{
public string token { get; set; }
}
class RandomData
{
public string ts { get; set; }
public string td { get; set; }
public string id { get; set; }
public string val { get; set; }
public string flags { get; set; }
public string type { get; set; }
public string uts { get; set; }
public string nr { get; set; }
}
Output:
Note: You need to install NewtonSoft NuGet package and import using Newtonsoft.Json.Linq; namespace to your program.
There is my version of answer:
var strData = #"[{
'token': 'NlhYnzF0xqG'
},
[{
'ts': '2019-03-21 14:06:22.123',
'id': 'CC_RTC_Second',
'val': '22'
}, {
'ts': '2019-03-21 14:06:00.096',
'id': 'CC_RTC_Minute',
}, {
'id': 'CC_RTC_Hour',
'val': '14'
}]
]";
var result = JsonConvert.DeserializeObject<List<JToken>>(strData);
var tokens = result.Where( x=> x.Type == JTokenType.Object).Select(x=> x.ToObject<Token>());
var innerData = result.Where(x => x.Type == JTokenType.Array).Select(x => x.ToObject<List<InnerData>>());
there are classes:
public class Token
{
public string token { set; get; }
}
public class InnerData
{
public string ts { set; get; }
public string id { set; get; }
public string val { set; get; }
}
You could print results as was mention before :
foreach (var token in tokens)
{
Console.WriteLine("ts: " + token.token);
}

c# data pull from json - can not detect the "-" sign between words

string json_index = '"libraries": [
{
"name": "test1",
"natives": {
"windows": "natives-windows"
},
"downloads": {
"classifiers": {
"natives-windows": {
"url": "http://test1.com/"
}
}
}
},
{
"name": "test2",
"natives": {
"windows": "natives-windows"
},
"downloads": {
"classifiers": {
"natives-windows": {
"url": "http://test2.com/"
}
}
}
}
]';
dynamic jsonObj = JsonConvert.DeserializeObject(json_index);
foreach (var obj in jsonObj.libraries)
{
label1.Text += "\n" + obj.downloads.classifiers.natives-windows.url; // error here
}
Can not detect the "-" sign between words.
I actually thought that:
string nativeswindows = obj.natives.windows;
label1.Text += "\n" + obj.downloads.classifiers.nativeswindows.url;
but it did not work
How can I get the "url" in "natives-windows" ?
I am using Newtonsoft JSON.
you try:
label1.Text += "\n" + obj.downloads.classifiers["natives-windows"].url;
I found this link: Parsing JSON w/ # at sign symbol in it (arobase)
Hope it will help you!
So there's a few steps to this.
First you need to define a concrete class to represent your JSON. I've done this using http://json2csharp.com, with the output being here:
public class Natives
{
public string windows { get; set; }
}
public class NativesWindows
{
public string url { get; set; }
}
public class Classifiers
{
public NativesWindows __invalid_name__natives-windows { get; set; }
}
public class Downloads
{
public Classifiers classifiers { get; set; }
}
public class Library
{
public string name { get; set; }
public Natives natives { get; set; }
public Downloads downloads { get; set; }
}
public class RootObject
{
public List<Library> libraries { get; set; }
}
Your problematic field has been flagged up by this tool too, seen here:
public NativesWindows __invalid_name__natives-windows { get; set; }
So we need a way to assign the JSON Key/Value pair to a valid C# field. We can does this using Attributes.
For this field in particular, we can use the JsonProperty attribute to take in the JSON property name and assign it to a C# field on your new concrete class. This looks like:
[JsonProperty("native-windows")]
public NativesWindows NativeWindowsObj { get; set; }
You can put that into your new concrete class, and then use the following to deserialize to that type:
Natives jsonObj = JsonConvert.DeserializeObject<Natives>(json_index);
This is telling Newtonsoft:
I have a property name native-windows.
I'm deserializing my JSON to this specific class.
The invalid C# identified native-windows matches a JsonProperty I've specified in my class, assign the value to that matching attribute.
Return the full, deserialized object.

Deserialize nested JSON into Dictionary, string and class

I have a JSON that I'd like to DeserializeObject into an outerDictionary with innerDictionary and innermostClass as so:
var entityMap = JsonConvert.DeserializeObject<Dictionary<string, Dictionary<string, fieldClass>>>(File.ReadAllText("map.json"));
However, the innerDictionary may have a string:string instead of string:innerMostClass.
{
"Client": {
"__class__": "contact",
"ClientId": {
"__field__": "new_ndisclientid",
"__type__": "string"
},
"GivenName": {
"__field__": "firstname",
"__type__": "string"
},
},
"Case": {
"__class__": "contact",
"CaseId": {
"__field__": "new_ndiscaseid",
"__type__": "string"
}
}
}
Is there a way to do this? I don't want to make all of it into Classes.
Is it possible to do this with a custom JsonConverter?
EDIT: Renamed classname to entityName for clarity. ClientId and GivenName would be deserialized into fieldClasses.
You can use dynamic or object instead of inner class
var json =
"{\r\n\t\"Client\": {\r\n\t\t\"__entityName__\": \"contact\",\r\n\r\n\t\t\"ClientId\": {\r\n\t\t\t\"__field__\": \"new_ndisclientid\",\r\n\t\t\t\"__type__\": \"string\"\r\n\t\t},\r\n\t\t\"GivenName\": {\r\n\t\t\t\"__field__\": \"firstname\",\r\n\t\t\t\"__type__\": \"string\"\r\n\t\t}\r\n\t}\r\n}";
var deserialized = JsonConvert.DeserializeObject<Dictionary<string, Dictionary<string, dynamic>>>(json);
List<object> values = deserialized.SelectMany(result => result.Value).Cast<object>().ToList();
If you want separate inner class
public class Client
{
public string __entityName__ { get; set; }
public Dictionary<string, string> ClientId { get; set; }
public Dictionary<string, string> GivenName { get; set; }
}
var deserializedWithClass = JsonConvert.DeserializeObject<Dictionary<string, Client>>(json);
Deserialize nested JSON into Class. not on dictionary based but it's useful.
Step 01: open the link https://jsonformatter.org/json-parser
Step 02: copy the down contents.
{
"Client": {
"__class__": "contact",
"ClientId": {
"__field__": "new_ndisclientid",
"__type__": "string"
},
"GivenName": {
"__field__": "firstname",
"__type__": "string"
}
},
"Case": {
"__class__": "contact",
"CaseId": {
"__field__": "new_ndiscaseid",
"__type__": "string"
}
}
}
Step 03: Open above link. copy contents and past in to left side and click on to JSON Parser button. Look like below image.
Step 04: Click on download button. Downloading the jsonformatter.txt file. Successfully download the file as jsonformatter.txt.
Step 05: Copy step 02 content and open url https://json2csharp.com/.Copy contents and past in to left side and click on to Convert button. Look like below image.
Step 06: In Scripting.
(A) Create myRootClass.cs file and copy and past down contents to your file.[[System.Serializable] it's used in unity 3d software c# scripting]
[System.Serializable]
public class myRootClass
{
[System.Serializable]
public class ClientId
{
public string __field__ { get; set; }
public string __type__ { get; set; }
}
[System.Serializable]
public class GivenName
{
public string __field__ { get; set; }
public string __type__ { get; set; }
}
[System.Serializable]
public class Client
{
public string __class__ { get; set; }
public ClientId ClientId { get; set; }
public GivenName GivenName { get; set; }
}
[System.Serializable]
public class CaseId
{
public string __field__ { get; set; }
public string __type__ { get; set; }
}
[System.Serializable]
public class Case
{
public string __class__ { get; set; }
public CaseId CaseId { get; set; }
}
[System.Serializable]
public class Root
{
public Client Client { get; set; }
public Case Case { get; set; }
}
}
(B) Read the jsonformatter.txt file.
// Read entire text file content in one string
string textFilePath = "C:/Users/XXX/Downloads/jsonformatter.txt";//change path
string jsontext = System.IO.File.ReadAllText(textFilePath);
Debug.Log("Read Json"+jsontext);// used Console.Writeline
(C) Convert string into C# and show the data.
Root myDeserializedClass = JsonConvert.DeserializeObject<Root>(jsontext);
var client = myDeserializedClass.Client;
Debug.Log("client.__class__ :- "+client.__class__); //used Console.Writeline
Debug.Log("client.ClientId.__field__ :- "+client.ClientId.__field__);// used Console.Writeline
Debug.Log("client.GivenName.__field__ :- "+client.GivenName.__field__);// used Console.Writeline

Deserialize JSON Object into Class

I'm having a little trouble deserializing a JSON object to a class (using JSON.NET), and hoping someone can point me in the right direction. Below is a snippet of code I'm trying, and been testing at dotnetfiddle
Here's a sample of the JSON:
{
"`LCA0001": {
"23225007190002": "1",
"23249206670003": "1",
"01365100070018": "5"
},
"`LCA0003": {
"23331406670018": "1",
"24942506670004": "1"
},
"`LCA0005": {
"01365100070018": "19"
}
}
I'm trying to use this code:
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
public class Program
{
public static void Main()
{
string json = "{\"`LCA0001\": {\"23225007190002\": \"1\",\"23249206670003\": \"1\",\"01365100070018\": \"5\"},\"`LCA0003\": {\"23331406670018\": \"1\",\"24942506670004\": \"1\"},\"`LCA0005\": {\"01365100070018\": \"19\"}}";
Console.WriteLine(json);
Console.WriteLine();
//This works
Console.Write("Deserialize without class");
var root = JsonConvert.DeserializeObject<Dictionary<string, Dictionary<string, int>>>(json);
foreach (var locationKvp in root)
{
foreach (var skuKvp in locationKvp.Value)
{
Console.WriteLine("location: " + locationKvp.Key + ", sku: " + skuKvp.Key + ", qty: " + skuKvp.Value);
}
}
//Why doesn't this work?
Console.Write("\nDeserialize with class");
var root2 = JsonConvert.DeserializeObject<InventoryLocations>(json);
foreach (var locationKvp in root2.InventoryLocation)
{
foreach (var skuKvp in locationKvp.Value)
{
Console.WriteLine("location: " + locationKvp.Key + ", sku: " + skuKvp.Key + ", qty: " + skuKvp.Value);
}
}
}
}
class InventoryLocations
{
public Dictionary<Location, Dictionary<Sku, Qty>> InventoryLocation { get; set; }
}
public class Location
{
public string location { get; set; }
}
public class Sku
{
public string sku { get; set; }
}
public class Qty
{
public int qty { get; set; }
}
Is there a reason why deserializing into a Class doesn't work? Am I just defining the classes incorrectly?
I see two problems here: one is using classes as the dictionary keys - the JSON has simple strings there (and cannot have anything else really), so that won't work.
The second problem is that deserialization of JSON to classes works by matching keys to properties - so it converts something like
{
"prop1": "value1",
"prop2": "value2"
}
to an instance of:
public class MyClass {
public string prop1 { get; set; }
public string prop2 { get; set; }
}
In your case this cannot work because in your JSON all keys are not valid property names. You have to stick with the deserialization to a dictionary
One of the ways to generate the classes from JSON is using Visual Studio.
Navigate to Edit -> Paste Special -> Paste JSON As Classes. For posted JSON in question, following classes are generated.
public class Rootobject
{
public LCA0001 LCA0001 { get; set; }
public LCA0003 LCA0003 { get; set; }
public LCA0005 LCA0005 { get; set; }
}
public class LCA0001
{
public string _23225007190002 { get; set; }
public string _23249206670003 { get; set; }
public string _01365100070018 { get; set; }
}
public class LCA0003
{
public string _23331406670018 { get; set; }
public string _24942506670004 { get; set; }
}
public class LCA0005
{
public string _01365100070018 { get; set; }
}
In addition to MiMo's answer, you can use a ContractResolver to serialize/deserialize Dictionaries within classes.
Here's a working example of your code in dotnetfiddle.
Note the serialized Json with the contract resolver is different than the original json. It must be serialized using this contract resolver in order to deserialize with it as well.
I pulled the contract resolver from this StackOverflow question, if you need any more clarification.

Cannot deserialize the JSON array

I have Json like below:
[
{
"name": "ts.DatumVon",
"value": "29.10.2015"
},
{
"name": "ts.Von",
"value": "8:00"
},
{
"name": "ts.Bis",
"value": "16:30"
}
]
for this class:
public class TSInfo
{
public TimeSaver ts { get; set; }
[Display(Name = "Status")]
public TSStatus tsStatus { get; set; }
[Display(Name = "Typ")]
public TSTyp tsTyp { get; set; }
public TSAuswahlSteps step { get; set; }
}
How to deserialize this Json string in controller method?
EDIT:
I hope that clarifies it.
public class TimeSaver
{
public DateTime DatumVon { get; set; }
public TimeSpan Von { get; set; }
public TimeSpan Bis { get; set; }
}
I tried something like this:
string tsi = [{"name":"ts.DatumVon","value":"29.10.2015"},{"name":"ts.Von","value":"8:00"},{"name":"ts.Bis","value":"16:30"}]
var dict = JsonConvert.DeserializeObject<List<Dictionary<String,String>>(tsi);
The JSON you provided is a list of dictionaries. So you can deserialize it (using NewtonSoft.Json) like this:
string json = "your json";
var result = JsonConvert.Deserialize<List<Dictionary<String,String>>(json);
How you map the result to your class is up to you.
EDIT the above makes no sense. Sorry for that.
Well, your JSON gave me some headache but I think I fixed it.
The JSON is an array of KeyValuePairs. Every pair describes an attribute of your TimeSaver class. The array as an whole describes the complete class. I don't know of an easy way to convert this JSON to a C# class. What complicates the problem even more is the fact that every attribute has some sort of namespace prefix: ts. The final complication is the date format. That's not a format that's recognized automatically.
My solution converts the JSON to a new JSON describing a TimeSaver object. This new JSON is then deserialized using JsonConvert.
One issue still remains: the TimeSaver.DateVon has become a string.
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
public class Program
{
public static void Main()
{
string tsi = "[{\"name\":\"ts.DatumVon\",\"value\":\"29.10.2015\"},{\"name\":\"ts.Von\",\"value\":\"8:00\"},{\"name\":\"ts.Bis\",\"value\":\"16:30\"}]";
var attributes = JsonConvert.DeserializeObject<List<NameValuePair>>(tsi);
attributes = attributes
.Select(item => new NameValuePair { Name = item.Name.Replace("ts.", ""), Value = item.Value })
.ToList();
var newJson = "{" + String.Join(",", attributes.Select(item => String.Format("\"{0}\":\"{1}\"", item.Name, item.Value))) + "}";
Console.WriteLine(newJson);
var ts = JsonConvert.DeserializeObject<TimeSaver>(newJson);
Console.WriteLine(ts.DatumVon);
Console.WriteLine(ts.Von);
Console.WriteLine(ts.Bis);
}
}
public class NameValuePair
{
public string Name { get; set; }
public string Value { get; set; }
}
public class TimeSaver
{
public String DatumVon { get; set; }
public TimeSpan Von { get; set; }
public TimeSpan Bis { get; set; }
}

Categories