Parse the JSON syntax if it is not parsed well - c#

How do I parse the JSON data if it is not well parsed from newtonsoft json. Please refer my below code:
var web_uri = new Uri("www.example.com");
var resp = await client2.GetAsync(web_uri);
var resp_to_str = await resp.Content.ReadAsStringAsync();
var json_obj = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(resp_to_str);
Finally i parsed the JSON. now, it produces as expected.
{
"Sex": "Male",
"category": "A",
"ID": 14,
"created": "2016-03-03",
"Tag": "2340",
"members": [{
"type": "A",
"name": "fam_mem",
"state": "ca",
"Family": {
"myGuardName": "tony",
"details": [{
"address": "ca",
"type": "A"
}]
}
}]
}
**RootObject omyclass = Newtonsoft.Json.JsonConvert.DeserializeObject<RootObject>(json_obj);**
Now i am getting error at the above line:
An exception of type 'Microsoft.CSharp.RuntimeBinder.RuntimeBinderException' occurred in System.Linq.Expressions.dll but was not handled in user code
Additional information: The best overloaded method match for 'Newtonsoft.Json.JsonConvert.DeserializeObject(string)' has some invalid arguments
public class Detail
{
public string address { get; set; }
public string type { get; set; }
}
public class Family
{
public string myGuardName { get; set; }
public List<Detail> details { get; set; }
}
public class Member
{
public string type { get; set; }
public string name { get; set; }
public string state { get; set; }
public Family Family { get; set; }
}
public class RootObject
{
public string Sex { get; set; }
public string category { get; set; }
public int ID { get; set; }
public string created { get; set; }
public string Tag { get; set; }
public List<Member> members { get; set; }
}
TextBlock.Text = omyclass
I have updated the question

Hope it helps:
var web_uri = new Uri("www.example.com");
var resp = await client2.GetAsync(web_uri);
var resp_to_str = await resp.Content.ReadAsStringAsync();
RootObject omyclass = JsonConvert.DeserializeObject<RootObject>(resp_to_str); //pass the response string here.
Updated from OP's comment:
textBlock2.Text = omyclass + "----!"; will not work because omyclass is RootObject, not a string
You have to get the info you need and append it to textBlock2:
textBlock2.text = omyclass.Sex + "----!";
UPDATE 2 (OP get string as key value pair):
Usage: textBlock2.text = omyclass + "----!";
Override RootObject.ToString() and use Reflection to get properties & property values
public class RootObject
{
public string Sex { get; set; }
public string category { get; set; }
public int ID { get; set; }
public string created { get; set; }
public string Tag { get; set; }
public List<Member> members { get; set; }
public override string ToString()
{
var values = new List<string>();
foreach (var property in GetType().GetProperties())
{
values.Add(property.Name + ": " + property.GetValue(this));
}
return string.Join(", ", values);
}
}

The problem is that JsonConvert.DeserializeObject<T>(string) expects a string as an input argument -- but you are not passing in a string. You are passing in json_obj which is a dynamic returned by a previous call to deserialize:
var json_obj = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(resp_to_str);
var omyclass = Newtonsoft.Json.JsonConvert.DeserializeObject<RootObject>(json_obj);
What JsonConvert returns from the first dynamic call is in fact a JToken containing a tree of LINQ-to-JSON tokens -- not a string. This causes the RuntimeBinderException upon making the second call.
There is no need to double-deserialize the JSON string in this manner. Just pass the resp_to_str to JsonConvert.DeserializeObject<RootObject>():
var omyclass = Newtonsoft.Json.JsonConvert.DeserializeObject<RootObject>(resp_to_str);
Prototype fiddle.
Update
If you want to see all the fields and properties of the deserialized class in a text box, you could re-serialize it to JSON:
var reserializedJson = JsonConvert.SerializeObject(omyclass, Formatting.Indented);
textBlock2.Text = reserializedJson;
If you do
textBlock2.Text = omyclass + "----!"
You are just showing the ToString() value for your class. And since you have not overridden this method it will just show the class name.
If you don't want to re-serialize you could use the following extension method:
public static class ObjectExtensions
{
public static StringBuilder ToStringWithReflection<T>(this T obj, StringBuilder sb)
{
sb = sb ?? new StringBuilder();
if (obj == null)
return sb;
if (obj is IEnumerable)
{
sb.Append("[");
var first = true;
foreach (var item in ((IEnumerable)obj))
{
if (!first)
sb.Append(",");
sb.Append(item == null ? "" : item.ToString());
first = false;
}
sb.Append("]");
}
else
{
var type = obj.GetType();
var fields = type.GetFields();
var properties = type.GetProperties().Where(p => p.GetIndexParameters().Length == 0 && p.GetGetMethod(true) != null && p.CanRead);
var query = fields
.Select(f => new KeyValuePair<string, object>(f.Name, f.GetValue(obj)))
.Concat(properties
.Select(p => new KeyValuePair<string, object>(p.Name, p.GetValue(obj, null))));
sb.Append("{").Append(obj.GetType().Name).Append(": ");
var first = true;
foreach (var pair in query)
{
if (!first)
sb.Append(", ");
sb.Append(pair.Key).Append(": ");
if (pair.Value is IEnumerable && !(pair.Value is string))
pair.Value.ToStringWithReflection(sb);
else
sb.Append(pair.Value == null ? "null" : pair.Value.ToString());
first = false;
}
sb.Append("}");
}
return sb;
}
public static string ToStringWithReflection<T>(this T obj)
{
return obj.ToStringWithReflection(new StringBuilder()).ToString();
}
}
Then do
textBlock2.Text = omyclass.ToStringWithReflection() + "----!"
Update 2
Or, if you want to include properties from your object hierarchy recursively, you can override the ToString() method of each, like so:
public class Detail
{
public string address { get; set; }
public string type { get; set; }
public override string ToString() { return this.ToStringWithReflection(); }
}
public class Family
{
public string myGuardName { get; set; }
public List<Detail> details { get; set; }
public override string ToString() { return this.ToStringWithReflection(); }
}
public class Member
{
public string type { get; set; }
public string name { get; set; }
public string state { get; set; }
public Family Family { get; set; }
public override string ToString() { return this.ToStringWithReflection(); }
}
public class RootObject
{
public string Sex { get; set; }
public string category { get; set; }
public int ID { get; set; }
public string created { get; set; }
public string Tag { get; set; }
public List<Member> members { get; set; }
public override string ToString() { return this.ToStringWithReflection(); }
}
Then the ToString() output will be:
{RootObject: Sex: Male, category: A, ID: 14, created: 2016-03-03, Tag: 2340, members: [{Member: type: A, name: fam_mem, state: ca, Family: {Family: myGuardName: tony, details: [{Detail: address: ca, type: A}]}}]}

You Can Use a Custom Json formatter and parse it the way you want.It can be implemented at framework level so that all data is parsed according to your requirements.Inherit the custom formatter from MediaTypeFormatter.
Implement the virtual and abstract functions of MediaTypeFormatter.Pasre the data according to your requirements here.
I have implemented the formatter this way and it even parses complex data to match the data that i want.

When serializing complex Json if not deserialized correctly the value becomes null.For deserializing complex Json use JsonMediaTypeFormatter.
My Example
In your config File add
config.Formatters.Clear();
config.Formatters.Insert(0, new JsonNetFormatterDecide()); // My custom formatter
config.Formatters.Insert(1, new JsonMediaTypeFormatter());//Default Formatter
config.MapHttpAttributeRoutes();`
So i used my custom formatter for all get operations.Format the data as i want and sent to client.The problem i faced was while accepting complex Json data(such as Jsonarrays within Jsonobjects). So in my config file i added the JsonMediaTypeFormatter() in index 1 of Config.formatters.
In my custom formatter
public override bool CanReadType(Type type)
{
return false;
}
This makes JsonMediaTypeFormatter which is a very popular formatter to Deserialize complex data to do the deserialization.
The bottomline is that you can use JsonMediaTypeFormatter toDeserialize complex Json Data
It has some predefined functions to deserialize.
By looking at your Json it seems as if Json arrays within Json objects is the reason why your Json is not deserialing correctly
If you plan to write your custom Json formatter ,You can implement at framework level like
public class JsonNetFormatterDecide : MediaTypeFormatter
{
//......
public override bool CanReadType(Type type)
{
return false; //this causes the // project to use the second formatter in the config file ie,JsonMediaTypeFormatter or the //default Json Formatter
}
}

Related

How can I convert url-encoded or url-decoded string to json format and then to object?

I'm getting a post request to my api (x-www-form-urlencoded) and the body of the request looks like this:
worker=%7B%22_id%22%3A+%7B%22%24oid%22%3A+%2261asd23e9231241dfd2b4c3bd%22%7D%2C+%22sid%22%3A+%22WKb32df49cas43413585352e8a6e2%cd%22%22%%22%3A+1234154123%7D%7D&task=%7B%22_id%22%3A+%7B%22%24oid%22%3A+%2261caffc34dsf33182b4c789
continues.
There are 2 objects (classes) that I need to receive in this incoming request, and I created the class structure of these 2 objects: For example, my class structure is as follows:
public class Worker
{
[JsonProperty("friendly_name")]
public string FriendlyName { get; set; }
[JsonProperty("date_updated")]
public WorkerDateUpdated DateUpdated { get; set; }
[JsonProperty("activity")]
public string Activity { get; set; }
[JsonProperty("workspace_sid")]
public string WorkspaceSid { get; set; }
[JsonProperty("date_created")]
public WorkerDateCreated DateCreated { get; set; }
[JsonProperty("queues")]
public List<string> queues { get; set; }
}
public class Task
{
[JsonProperty("reason")]
public string Reason { get; set; }
[JsonProperty("date_updated")]
public TaskDateUpdated DateUpdated { get; }
[JsonProperty("assignment_status")]
public string AssignmentStatus { get; set; }
[JsonProperty("total_cost")]
public TaskTotalCost TotalCost { get; set; }
}
In the incoming request, I receive 3 objects (class) as url-encoded, I only need 2 objects and their properties.
using (var reader = new StreamReader(
HttpContext.Request.Body,
encoding: Encoding.UTF8,
detectEncodingFromByteOrderMarks: false
))
{
var bodyString = await reader.ReadToEndAsync();
_logger.LogInformation("BodyString ---> " + bodyString);
var decodedUrl = HttpUtility.UrlDecode(bodyString);
_logger.LogInformation(" decodedUrl ---> " + decodedUrl);
}
I can read the incoming body and convert it to decoded format. Below is an example:
worker={"_id": {"$oid": "XXXXXXXXXX"}, "sid": "XXXXXXXXXXXXX", "x": true, "account_sid": "XXXXXXXXXXXX", "workspace_sid": "XXXXXXXXXXXX", "queues ": ["XXXXXXXXXXXXXX"], "activity": "idle", "available": true, "friendly_name": "XXXXXXXX", "attributes": {"mail": "XXXXXXXXXXXX", "name": "XXXXXXXXXX" }, "date_created": {"$date": XXXXXXXXX}, "date_updated": {"$date": XXXXXXXXXX}, "date_status_changed": {"$date": XXXXXXXXXXXXX}}&task={"_id": {" $oid": "XXXXXXXXXXXXXX"}, "sid": "XXXXXXXXXXXX", "x": true, "account_sid": "XXXXXXXXXXX", "workspace_sid": "XXXXXXXXXXXXXXXXXX", "workflow_sid": "XXXXXXXXXXXX", "workflow_friendly_name" : "daytime1", "initial_attributes": {"station_name": "XXXXX", "component_type": X, "component_id": XXX, "mail": "XXXXXX", "main_issue": "XXXXXXXXXXXXXX", "predictivi_maintenance_time": "XXXXXXXXXX", "hospital_name": "\u00dcsk\u00fcdar XXXXXXXXXXX"}
I can see it as , but I can't deserialize it. Or I don't know if I'm doing it wrong. I have created a separate class that contains my 2 classes. I keep my Worker and Task class in it, I cannot deserialize to that class, it does not deserialize in any way. Unexpected charachter throws exception. How can I convert these objects to json format or object format?
Edit:
My Other Custom classes:
public class TaskDateUpdated
{
[JsonProperty("$date")]
public long Date { get; set; }
}
public class TaskTotalCost
{
[JsonProperty("$numberDecimal")]
public string NumberDecimal { get; set; }
}
public class TaskDateCreated
{
[JsonProperty("$date")]
public long Date { get; set; }
}
public class TaskLastChargeDate
{
[JsonProperty("$date")]
public long Date { get; set; }
}
public class TaskId
{
[JsonProperty("$oid")]
public string Oid { get; set; }
}
public class WorkerDateUpdated
{
[JsonProperty("$date")]
public long date { get; set; }
}
public class WorkerDateCreated
{
[JsonProperty("$date")]
public long date { get; set; }
}
public class WorkerDateStatusChanged
{
[JsonProperty("$date")]
public long date { get; set; }
}
I also have a single class containing these 2 classes, I get an error when I try to deserialize to this class, I also get an error when I try to deserialize it to other worker and task classes separately. I can't deserialize at all.
public class DataContainer
{
public Task Task { get; set; }
public Worker Worker { get; set; }
}
My post method looks like this:
[HttpPost]
[Consumes("application/x-www-form-urlencoded")]
public async Task<ActionResult<ResponseRequest>> AddWorkerTask()
{
using (var reader = new StreamReader(
HttpContext.Request.Body,
encoding: Encoding.UTF8,
detectEncodingFromByteOrderMarks: false
))
{
var bodyString = await reader.ReadToEndAsync();
_logger.LogInformation("BodyString ---> " + bodyString);
var decoded = HttpUtility.UrlDecode(bodyString);
//here is where i need to deserialize and convert it to
//a valid json and object
}
}
You can use HttpUtility.ParseQueryString to parse it into a NameValueCollection, then simply use the indexer ["worker"] on that.
[HttpPost]
[Consumes("application/x-www-form-urlencoded")]
public async Task<ActionResult<ResponseRequest>> AddWorkerTask()
{
using (var reader = new StreamReader(
HttpContext.Request.Body,
encoding: Encoding.UTF8,
detectEncodingFromByteOrderMarks: false
))
{
var bodyString = await reader.ReadToEndAsync();
var decoded = HttpUtility.ParseQueryString(bodyString);
var worker = JsonSerializer.Deserialize<Worker>(decoded["worker"]);
var task = JsonSerializer.Deserialize<Task>(decoded["task"]);
// or whatever your JSON deserializer is
}
}
In newer versions of ASP.net Core you can use HttpContext.Request.Form
[HttpPost]
[Consumes("application/x-www-form-urlencoded")]
public async Task<ActionResult<ResponseRequest>> AddWorkerTask()
{
var worker = JsonSerializer.Deserialize<Worker>(HttpContext.Request.Form["worker"]);
var task = JsonSerializer.Deserialize<Task>(HttpContext.Request.Form["task"]); // or whatever your JSON deserializer is
}

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.

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 value is sometimes a string and sometimes an object

I have some JSON that can come in two different formats. Sometimes the location value is a string, and sometimes it is an object. This is a sample of the first format:
{
"result": [
{
"upon_approval": "Proceed to Next Task",
"location": "",
"expected_start": ""
}
]
}
Class definitions for this:
public class Result
{
public string upon_approval { get; set; }
public string location { get; set; }
public string expected_start { get; set; }
}
public class RootObject
{
public List<Result> result { get; set; }
}
Here is the JSON in the second format:
{
"result": [
{
"upon_approval": "Proceed to Next Task",
"location": {
"display_value": "Corp-HQR",
"link": "https://satellite.service-now.com/api/now/table/cmn_location/4a2cf91b13f2de00322dd4a76144b090"
},
"expected_start": ""
}
]
}
Class definitions for this:
public class Location
{
public string display_value { get; set; }
public string link { get; set; }
}
public class Result
{
public string upon_approval { get; set; }
public Location location { get; set; }
public string expected_start { get; set; }
}
public class RootObject
{
public List<Result> result { get; set; }
}
When deserializing, I get errors when the JSON format does not match my classes, but I don't know ahead of time which classes to use because the JSON format changes. So how can I dynamically get these two JSON formats to deserialize into one set of classes?
This is how I am deserializing now:
JavaScriptSerializer ser = new JavaScriptSerializer();
ser.MaxJsonLength = 2147483647;
RootObject ro = ser.Deserialize<RootObject>(responseValue);
To solve this problem you'll need to make a custom JavaScriptConverter class and register it with the serializer. The serializer will load the result data into a Dictionary<string, object>, then hand off to the converter, where you can inspect the contents and convert it into a usable object. In short, this will allow you to use your second set of classes for both JSON formats.
Here is the code for the converter:
class ResultConverter : JavaScriptConverter
{
public override IEnumerable<Type> SupportedTypes
{
get { return new List<Type> { typeof(Result) }; }
}
public override object Deserialize(IDictionary<string, object> dict, Type type, JavaScriptSerializer serializer)
{
Result result = new Result();
result.upon_approval = GetValue<string>(dict, "upon_approval");
var locDict = GetValue<IDictionary<string, object>>(dict, "location");
if (locDict != null)
{
Location loc = new Location();
loc.display_value = GetValue<string>(locDict, "display_value");
loc.link = GetValue<string>(locDict, "link");
result.location = loc;
}
result.expected_start = GetValue<string>(dict, "expected_start");
return result;
}
private T GetValue<T>(IDictionary<string, object> dict, string key)
{
object value = null;
dict.TryGetValue(key, out value);
return value != null && typeof(T).IsAssignableFrom(value.GetType()) ? (T)value : default(T);
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
throw new NotImplementedException();
}
}
Then use it like this:
var ser = new JavaScriptSerializer();
ser.MaxJsonLength = 2147483647;
ser.RegisterConverters(new List<JavaScriptConverter> { new ResultConverter() });
RootObject ro = serializer.Deserialize<RootObject>(responseValue);
Here is a short demo:
class Program
{
static void Main(string[] args)
{
string json = #"
{
""result"": [
{
""upon_approval"": ""Proceed to Next Task"",
""location"": {
""display_value"": ""Corp-HQR"",
""link"": ""https://satellite.service-now.com/api/now/table/cmn_location/4a2cf91b13f2de00322dd4a76144b090""
},
""expected_start"": """"
}
]
}";
DeserializeAndDump(json);
Console.WriteLine(new string('-', 40));
json = #"
{
""result"": [
{
""upon_approval"": ""Proceed to Next Task"",
""location"": """",
""expected_start"": """"
}
]
}";
DeserializeAndDump(json);
}
private static void DeserializeAndDump(string json)
{
var serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new List<JavaScriptConverter> { new ResultConverter() });
RootObject obj = serializer.Deserialize<RootObject>(json);
foreach (var result in obj.result)
{
Console.WriteLine("upon_approval: " + result.upon_approval);
if (result.location != null)
{
Console.WriteLine("location display_value: " + result.location.display_value);
Console.WriteLine("location link: " + result.location.link);
}
else
Console.WriteLine("(no location)");
}
}
}
public class RootObject
{
public List<Result> result { get; set; }
}
public class Result
{
public string upon_approval { get; set; }
public Location location { get; set; }
public string expected_start { get; set; }
}
public class Location
{
public string display_value { get; set; }
public string link { get; set; }
}
Output:
upon_approval: Proceed to Next Task
location display_value: Corp-HQR
location link: https://satellite.service-now.com/api/now/table/cmn_location/4a2cf91b13f2de00322dd4a76144b090
----------------------------------------
upon_approval: Proceed to Next Task
(no location)

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