C# : How to Deserialize json data - c#

How to deserialized this type of json file usig C#
My Json
[{"data":{"CRC":"a459","PC":"3000","TID":"e2806810200000040a0652c8","antenna":3,"channel":922.75,"eventNum":396,"format":"epc","idHex":"e28068100000003c0a0652c8","peakRssi":-36,"phase":0.0,"reads":36},"timestamp":"2022-09-19T09:03:26.445+0700","type":"SIMPLE"}]
My Model
public class TagRead{
public string TID { get; set; }
}
public class Hdr{
public List<TagRead> data { get; set; }
}
My Controller
public ActionResult RefreshData()
{
string filepath = GetAndGenFilePath();
FileInfo fileInfo = new FileInfo(filepath);
bool isFileLocked = IsFileLocked(fileInfo);
if (isFileLocked)
{
return PartialView("TableTagView", GetListTag);
}
string rawData = System.IO.File.ReadAllText(filepath);
string[] lines = rawData.Split(';');
if (lines[0] == "")
{
HttpContext.Session["GetListTag"] = new List<Root>();
HttpContext.Session["LastChar"] = 0;
}
if (lines.Length > 0 && lines[0] != "" && LastChar != rawData.Length)
{
HttpContext.Session["GetListTag"] = new List<Root>();
foreach (var line in lines)
{
var deserialized = Newtonsoft.Json.JsonConvert.DeserializeObject<Hdr>(line);
var tag = deserialized.data.Select(s => new Root
{
TID = s.TID
}).ToList();
GetListTag.AddRange(tag);
}
HttpContext.Session["LastChar"] = rawData.Length;
}
return PartialView("TableTagView", GetListTag);
}
This json is from file, I want to get value then add into list
the values I want to get is TID,antenna,idHex
Thank you

This json is a collection so you'll need to deserialise it to a collection:
// List<Hdr>, not Hdr
var listOfHrds = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Hdr>>(json);
public class Hdr{
public TagRead data { get; set; }
}
Test:
var json = File.ReadAllText("data.json");
var listOfHdrs = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Hdr>>(json);
Console.WriteLine(listOfHdrs[0].data.TID); // Output: e2806810200000040a0652c8
public class Hdr{
public TagRead? data { get; set; }
}
public class TagRead{
public string? TID { get; set; }
}

if you need just one value, you don't need any custom classes at all
Console.WriteLine( "TID: " + JArray.Parse(json)[0]["data"]["TID"].ToString() );

Related

Iteration cannot operate on variables of type public definition for 'getenumerator'

I am doing a search in which I am making an API call and get the XML response and SerializeXmlNode and DeserializeObject to my root object. Now the problem is when I tried to loop with foreach.
I get this error below:
foreach statement cannot operate on variables of type (Model.AccountLite) because does not contain public instance definition for 'getenumerator'
I have inspected this data = JsonConvert.DeserializeObject(json); and i can see the data.
I have tried to look at this previously asked question
Search API call
public static List<AccountLite> searchAccounts(string searchString)
{
List<AccountLite> result = new List<AccountLite>();
Root data = new Root();
string[] contains = searchString.Split(' ');
RestClient client = new RestClient(baseUrl);
foreach (string contain in contains)
{
if (contain.Length < 3) continue;
RestRequest request = new RestRequest($"/xx/xx/xx/xxx/xxx/account?xx=Lite&searchString={searchString}");
String encoded = System.Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(username + ":" + password));
request.AddHeader("Authorization", "Basic " + encoded);
IRestResponse response = client.Execute(request);
string requestResponse = response.Content;
//Converting data from XML into Json and deserializet json object
try
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(requestResponse);
string json = JsonConvert.SerializeXmlNode(doc);
data = JsonConvert.DeserializeObject<Root>(json);
}
catch (Exception)
{
continue;
}
if (data?.SiebelMessageEnvelope?.ListOfAccountLite?.AccountLite == null)
continue;
//this line is the one showing error.
foreach (AccountLite item in data.SiebelMessageEnvelope.ListOfAccountLite.AccountLite)
{
bool containsBoth = true;
foreach (string contain2 in contains)
{
if (!item.Name.ToLower().Contains(contain2.ToLower()) && !item.Id.ToLower().Contains(contain2.ToLower()))
containsBoth = false;
}
if (containsBoth)
{
if (result.FirstOrDefault(i => i.Id == item.Id) == null)
{
result.Add(item);
}
}
}
}
return result;
}
Model
public class AccountLite
{
public string Id { get; set; }
public string AccountStatus { get; set; }
public string AccountTypeCode { get; set; }
public string Location { get; set; }
public string Name { get; set; }
public string SRIntegrationFlag { get; set; }
}
public class ListOfAccountLite
{
public AccountLite AccountLite { get; set; }
}
public class SiebelMessageEnvelope
{
[JsonProperty("#xmlns")]
public string Xmlns { get; set; }
public ListOfAccountLite ListOfAccountLite { get; set; }
}
public class Root
{
public SiebelMessageEnvelope SiebelMessageEnvelope { get; set; }
}
Json Object
{
"SiebelMessageEnvelope":{
"#xmlns":"",
"ListOfAccountLite":{
"AccountLite":{
"Id":"",
"AccountStatus":"",
"AccountTypeCode":"",
"Location":"",
"Name":"",
"SRIntegrationFlag":""
}
}
}
}
Your ListOfAccountLite just contains a single AccountLite. It doesn't make sense to foreach over a single object, where that object is not enumerable (meaning: implemented IEnumerable[<T>] or contains an explicit GetEnumerator() method).
There's only one object, so... just take it. Instead of
if (data?.SiebelMessageEnvelope?.ListOfAccountLite?.AccountLite == null)
continue;
foreach (AccountLite item in data.SiebelMessageEnvelope.ListOfAccountLite.AccountLite)
{
// ... do the thing
}
simply
var item = data?.SiebelMessageEnvelope?.ListOfAccountLite?.AccountLite;
if (item is null)
continue;
// ... do the thing
That said: you should probably investigate whether ListOfAccountLite in the JSON etc is meant to be an array rather than a single object.

Cannot create an Abstract class when trying to create List<T> from CSV using csv.GetRecords()

I'm attempting to create a generic method to enable me to parse a CSV document into an object of my choice.
Everything seems to work ok but the results after executing the csv.GetRecords() method are empty and the inner exception of the response is "Instances of abstract classes cannot be created."
I've also tried using the csv.EnumerateRecords(record); and get the same result.
public class ImportManager
{
[Ignore]
public string FileSeperator { get; set; }
[Ignore]
public string Filename { get; set; }
public IEnumerable<T> ParseFile<T>() where T : class
{
using (var reader = new StreamReader(this.Filename))
using (var csv = new CsvReader(reader))
{
csv.Configuration.Delimiter = this.FileSeperator;
csv.Configuration.HasHeaderRecord = true;
var results = csv.GetRecords<T>();
return results;
}
}
}
public class MyObject : ImportManager
{
public string Field1 { get; set; }
public DateTime Field2 { get; set; }
public int Field3 { get; set; }
public List<MyObject> LoadFile()
{
var response = ParseFile<MyObject>();
return response.ToList<MyObject>();
}
}
MyObject moObjList= new MyObject() { Filename = "MyFileName.txt", FileSeperator = "|" };
var results = moObjList.LoadFile();
Help!
I believe adding ToList() to csv.GetRecords<T>() may solve your issue. GetRecords<T>() does a lazy load. It doesn't attempt to enumerate the records until you call return response.ToList<MyObject>(); at which time the StreamReader is already disposed.
public class ImportManager
{
[Ignore]
public string FileSeperator { get; set; }
[Ignore]
public string Filename { get; set; }
public IEnumerable<T> ParseFile<T>() where T : class
{
using (var reader = new StreamReader(this.Filename))
using (var csv = new CsvReader(reader))
{
csv.Configuration.Delimiter = this.FileSeperator;
csv.Configuration.HasHeaderRecord = true;
var results = csv.GetRecords<T>().ToList();
return results;
}
}
}

From JSON string to object

I have a following valid json string:
var result = "[{\"total\":" + wpTotal + ",\"totalpages\":" + wpTotalPages + "},{\"tags\":[{\"id\":384},{\"id\":385}]}]";
I am trying to use this method to deserialize it to object of type T:
public static T FromJSON<T>(string json)
{
using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(json)))
{
var settings = new DataContractJsonSerializerSettings
{
UseSimpleDictionaryFormat = true
};
var serializer = new DataContractJsonSerializer(typeof(T), settings);
return (T)serializer.ReadObject(ms);
}
}
And using it like this:
var obj = JsonHelper.FromJSON<TagsResponse>(result);
I am always getting empty result (null) without any exception or any kind of information.
My model is:
[DataContract]
public class TagsResponse
{
[DataMember(Name = "total")]
public int Total { get; set; }
[DataMember(Name = "totalpages")]
public int TotalPages { get; set; }
[DataMember(Name = "tags")]
public List<Tag> Tags { get; set; }
}
Where is the problem?
Something is wrong with the format of your input, the properties in the json string are not the expected level of your objects, here is a working example:
class Program
{
static void Main(string[] args)
{
var json = "{\"total\":" + 1 + ",\"totalpages\":" + 10 + ",\"tags\":[{\"id\":384},{\"id\":385}]}";
var result = json.FromJSON<TagsResponse>();
Console.WriteLine("Hello World!" + result);
}
}
public static class Helper
{
public static T FromJSON<T>(this string json)
{
var serializer = new DataContractJsonSerializer(typeof(T));
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(json)))
{
return (T)serializer.ReadObject(ms);
}
}
}
[DataContract]
public class TagsResponse
{
[DataMember(Name = "total")]
public int Total { get; set; }
[DataMember(Name = "totalpages")]
public int TotalPages { get; set; }
[DataMember(Name = "tags")]
public List<Tag> Tags { get; set; }
}
[DataContract]
public class Tag
{
[DataMember(Name = "id")]
public string Id { get; set; }
}
1) Create correct json
var result = "[{\"total\":" + wpTotal + ",\"totalpages\":" + wpTotalPages + ",\"tags\":[{\"id\":384},{\"id\":385}]}]";
2) Parse it as array
var obj = JsonHelper.FromJSON<TagsResponse[]>(result);
Or, if you want to parse it as single item, you can remove [] from json
var result = "{\"total\":" + wpTotal + ",\"totalpages\":" + wpTotalPages + ",\"tags\":[{\"id\":384},{\"id\":385}]}";
var obj = JsonHelper.FromJSON<TagsResponse>(result);

Get the values of dynamic keys from a JSON string

I have this json string and i want to get the 4th line (iValue, sValue) of every record.
My problem here is the keys vary for every record (based on the data type of the value).
Is there any way to do this on C#?
Here is an example:
{ "data": [
{
"pKey": "0",
"Entity": "tableName",
"Attribute": "CID",
"iValue": "13"
},
{
"pKey": "0",
"Entity": "tableName",
"Attribute": "username",
"sValue": "test_user1"
}] }
Here is kind of a big implementation, you will have to implement this for each iValue, fValue, etc however, it speeds up the implementation and usage. First of, here is the usage:
string rawJson = "{\"data\":[{\"pKey\":\"0\",\"Entity\":\"tableName\",\"Attribute\":\"CID\",\"iValue\":\"13\"},{\"pKey\":\"0\",\"Entity\":\"tableName\",\"Attribute\":\"username\",\"sValue\":\"test_user1\"}]}";
var values = JsonConvert.DeserializeObject<TakeData>(rawJson).Data.Select(v => v.PureData);
Now values contains the list. Here is the usage for accessing each:
foreach (var val in values)
{
if (val is IntData i)
{
int myInt = i.iValue;
// use the rest of the properties
}
else if (val is StrData s)
{
string myStr = s.sValue;
// use the rest of the properties
}
}
And here is the implementation:
class TakeData
{
public List<TakeItAll> Data { get; set; }
}
class TakeItAll
{
public int pKey { get; set; }
public string Entity { get; set; }
public string Attribute { get; set; }
private int _iValue;
public int iValue
{
get => _iValue;
set
{
_iValue = value;
PureData = new IntData { pKey = pKey, Entity = Entity, Attribute = Attribute, iValue = iValue };
}
}
private string _sValue;
public string sValue
{
get => _sValue;
set
{
_sValue = value;
PureData = new StrData { pKey = pKey, Entity = Entity, Attribute = Attribute, sValue = sValue };
}
}
public IPureData PureData { get; private set; }
}
interface IPureData
{
int pKey { get; set; }
string Entity { get; set; }
string Attribute { get; set; }
}
class IntData : IPureData
{
public int pKey { get; set; }
public string Entity { get; set; }
public string Attribute { get; set; }
public int iValue { get; set; }
}
class StrData : IPureData
{
public int pKey { get; set; }
public string Entity { get; set; }
public string Attribute { get; set; }
public string sValue { get; set; }
}
Of course you can use some alternatives as well. Such as using an enum in TakeItAll to keep track of the data type (or a type variable) instead of so many classes. This way However the size of the values object would be larger.
class TakeItAll
{
public int pKey { get; set; }
public string Entity { get; set; }
public string Attribute { get; set; }
private int _iValue;
public int iValue
{
get => _iValue;
set
{
_iValue = value;
ValType = typeof(string);
}
}
private string _sValue;
public string sValue
{
get => _sValue;
set
{
_sValue = value;
ValType = typeof(int);
}
}
public Type ValType { get; private set; }
}
I would deserialize this into an object supporting both types of properties and then by code try parsing either the integer or the string if the integer fails.
If the Attribute value gives you a clue as to which one to look for, you could also use that to prevent having to try parsing the integer every time.
I would not rely on the property being the "fourth" property every time, as I'm assuming this would be external data, where you may not be able to control whether these properties come out in the exact same order every time (now and in the future).
If you don't know the data type then you could use an object to handle it.
It's a good idea to deserialize JSON string to concrete class to avoid string manipulation mistake.
public class Datum
{
public object pKey { get; set; }
public string Entity { get; set; }
public string Attribute { get; set; }
public string iValue { get; set; }
public string sValue { get; set; }
}
public class DataCollection
{
public List<Datum> data { get; set; }
}
public void Test()
{
var str = "{\"data\":[{\"pKey\":\"0\",\"Entity\":\"tableName\",\"Attribute\":\"CID\",\"iValue\":\"13\"},{\"pKey\":\"0\",\"Entity\":\"tableName\",\"Attribute\":\"username\",\"sValue\":\"test_user1\"}]}";
var list = JsonConvert.DeserializeObject<DataCollection>(str);
var keys = list.data.Select(x => x.pKey).ToList();
}
Another option is to deserialize to dynamic and inspect that:
var json = "{\"data\":[{\"pKey\":\"0\",\"Entity\":\"tableName\",\"Attribute\":\"CID\",\"iValue\":\"13\"},{\"pKey\":\"0\",\"Entity\":\"tableName\",\"Attribute\":\"username\",\"sValue\":\"test_user1\"}]}";
var result = JsonConvert.DeserializeAnonymousType<dynamic>(json, null);
if (result.data != null)
{
for (var i = 0; i < result.data.Count; i++)
{
if (result.data[i]["iValue"] != null)
// Parse iValue
if (result.data[i]["sValue"] != null)
// Parse sValue
}
}
You could load the Json in a ExpandoObject
var expConverter = new ExpandoObjectConverter();
dynamic objList = JsonConvert.DeserializeObject<List<ExpandoObject>>(json, expConverter);
JSON array to ExpandoObject via JSON.NET
Then once you have loaded it in as a List<ExpandoObject> you may itterate over it as a dictionary.
foreach(var obj in objList)
{
//convert the object to a Dictionary and select the 4th element.
var yourresult = (obj as IDictionary<string, object>).ElementAt(3);
}
My two cents:
Get each object of the array as a KeyValuePair
var json = "your json here";
var root = JsonConvert.DeserializeObject<Root>(json);
foreach (var element in root.Data)
{
//===================================================> Here using object because your value type change. You can change it to string if your value is always wrapped in a string (like "13")
var keyValuePair = element.ToObject<Dictionary<string, object>>();
//here, for each object of the 'data' array, you can check if the desidered property exists
if (keyValuePair.ContainsKey("iValue"))
{
var propertyValue = keyValuePair["iValue"];
}
else if (keyValuePair.ContainsKey("sValue"))
{
var propertyValue = keyValuePair["sValue"];
}
// Or you can check the property name in the desidered position
if (keyValuePair.Keys.ElementAt(3) == "iValue")
{
var propertyValue = keyValuePair["iValue"];
}
else if (keyValuePair.Keys.ElementAt(3) == "sValue")
{
var propertyValue = keyValuePair["sValue"];
}
}
Where Root is
class Root
{
[JsonProperty("data")]
public List<JObject> Data { get; set; }
}
With this solution you can always know which property (iValue or sValue) is specified. On the contrary, if you use a model class which has both property names, you wouldn't know which property is specified when the value is null (unless you use additional properties/classes and a custom JsonConverter).
Edit
As Panagiotis Kanavos reminded me, the JObject class implements IDictionary<string, JToken>. So, inside your foreach you could use:
if (element["iValue"] != null)
{
var propertyValue = element["iValue"].Value<string>();
}
if (element["sValue"] != null)
{
var propertyValue = element["sValue"].Value<string>();
}
// Or you can check the property name in the desidered position
var propName = element.Properties().ElementAt(3).Name;
if (propName == "iValue")
{
var propertyValue = keyValuePair["iValue"].Value<string>();
}
else if (propName == "sValue")
{
var propertyValue = keyValuePair["sValue"].Value<string>();
}
Of course you can optimize this code and check for nulls.

How to extract the data from Generic <T> List object

I wanna create the generic function inwhich I will just pass the class name and get the data in CSV format that is from JSON object on the bases of Generic Class Type.
But I am not able to access the class member while foreach loop.
public class Lifetouch
{
public int LifetouchID { get; set; }
public string Data { get; set; }
}
public class Lifetemp
{
public int LifetempID { get; set; }
public string Data { get; set; }
}
main()
{
getPerodicListofVitalSigns <Lifetouch>(new Lifetouch());
}
public static void getPerodicListofVitalSigns <T>( T clazz)
{
List<T> list_of_measurements_Original = JsonConvert.DeserializeObject<List<T>>(json_response);
// Got the list_of_measurements_Original count 2.
StringBuilder sb = new StringBuilder();
sb.Append("[");
foreach (var element in list_of_measurements_Original)
{
sb.Append(element.LifetouchID + ", ") // Not able to access the element of list LifetouchID
}
sb.Append("]");
}
I would like to create the generic function inwhich I will just pass the class name and get the data (ID's) in CSV format that is from JSON object on the bases of Generic Class Type. That all the ids will pass through webservice and update the database. Its the requirement.
I would like to create the Generic function in which I would like to pass the class name as a paramenter. So in future its easy for me to use that generic function if new class added.
Now its working fine..
public class Lifetouch
{
public int LifetouchID { get; set; }
public string Data { get; set; }
}
public class Lifetemp
{
public int LifetempID { get; set; }
public string Data { get; set; }
}
main()
{
getPerodicListofVitalSigns <Lifetouch>(new Lifetouch());
getPerodicListofVitalSigns <Lifetemp>(new Lifetemp());
}
public static void getPerodicListofVitalSigns <T>( T clazz)
{
List<T> list_of_measurements_Original = JsonConvert.DeserializeObject<List<T>>(json_response);
// Got the list_of_measurements_Original count 2.
StringBuilder sb = new StringBuilder();
sb.Append("[");
foreach (var element in list_of_measurements_Original)
{
if (VitalSignName == "Lifetouch")
{
var vitalSign = element as Lifetouch;
dataString = dataString + (vitalSign.LifetouchID + ",");
}
else if (VitalSignName == "Lifetemp")
{
var vitalSign = element as Lifetemp;
dataString = dataString + (vitalSign.LifetempID + ",");
}
}
sb.Append("]");
string WebserviceAddress = "192..../json/reply/UpdateSyncStatus";
JSON_POST_Sender.ClassPost(WebserviceAddress, dataString), "true")
}

Categories

Resources