C# Newtonsoft.Json DeserializeObject if json contains an invalid token [duplicate] - c#

This question already has answers here:
How can I use a reserved keyword as an identifier in my JSON model class?
(3 answers)
Closed 1 year ago.
How can i deserialize this json in C# using Newtonsoft.Json.JsonConvert.DeserializeObject?
The problem is that i cannot use "event" as a class property because its an invalid token.
{
"resultsPage": {
"results": {
"event": {
"location": {
"city": "London, UK",
"lng": -0.1150322,
"lat": 51.4650846
},
"uri": "http://www.....",
"displayName": "Test",
"id": 3037536,
"type": "Testtype",
"start": {
"time": "19:30:00",
"date": "2010-02-16",
"datetime": "2010-02-16T19:30:00+0000"
},
"status": "ok"
}
},
"status": "ok"
}
}

There's a few ways you could do this with Newtonsoft.
JsonConvert.DeserializeObject will return an object of type JObject, this can be done either by not specifying a type (instead casting after deserializing) or by specifying the JObject type.
From here you can access event because it is simply a key name, but you lose any strong typing of your json.
var jobj = (JObject)Newtonsoft.Json.JsonConvert.DeserializeObject(json);
var _event = jobj["resultsPage"]["results"]["event"];
Taking this a step further, you can use JsonConvert.DeserializeAnonymousType and specify the definition of your anonymous type, using #event or Event where event is present in the json. When accessing the property, you would need to use #event or Event depending on which you chose, and you gain the benefits of strongly typed objects.
var jobj = Newtonsoft.Json.JsonConvert.DeserializeAnonymousType(json, new {
resultsPage = new {
results = new {
#event = new {
location = new {
city = "",
lng = 0.0,
lat = 0.0
},
uri = "",
displayName = "",
id = 0,
type = "",
start = new {
time = "",
date = "",
datetime = new DateTime()
},
status = ""
}
},
status = ""
}
});
var _event = jobj.resultsPage.results.#event;
Next, you could create classes take this anonymous object definition and split it out into classes, again using #event or Event and it will deserialize.
var jobj = Newtonsoft.Json.JsonConvert.DeserializeObject<JsonClass>(json);
var _event = jobj.resultsPage.results.#event;
public class JsonClass
{
public ResultsPage resultsPage { get; set; }
public string status { get; set; }
}
public class ResultsPage
{
public Results results { get; set; }
public string status { get; set; }
}
public class Results
{
public Event #event { get; set; }
}
public class Event
{
public Location location { get; set; }
...
}
public class Location
{
public string city { get; set; }
}
Or you could look at using a property attribute to map a completely different property name to the json key (the below is a modified excerpt of the above).
public class Results
{
[JsonProperty(PropertyName = "event")]
public EventResult EventResult { get; set; }
}
public class EventResult
{
public Location location { get; set; }
}

Related

How to deserialise or format WebAPI result into specific json structure

I was working with a .net core 3.1 Web API. Which is getting data from an external API. Following is my code Controller part
[HttpGet("transinfo/{id}")]
public Object GettransactionData(int id)
{
var result=_transaction.GettransactionDetails(id).Result;
List<PipeLineResponse> P = JsonConvert.DeserializeObject<List<PipeLineResponse>>(result.ToString());
PipeLineResponseObject P1 = new PipeLineResponseObject();
P1.data = P;
return P1;
}
And my service code as follows
public async Task<Object> GettransactionDetails(int id)
{
string request=//fetched from db
var stringContent = new StringContent(request);
Client = utilities.GetHttpClient();
string apiEndpoint=//External API URL
HttpResponseMessage httpResponseMessage = await Client.PostAsync(apiEndpoint, stringContent);
if (httpResponseMessage.IsSuccessStatusCode)
{
return await httpResponseMessage.Content.ReadAsAsync<Object>();
}
}
But i am getting the result in following format (response from postman)
{
"data": [
{
"Tranid": "34540d40-7db8-44c1-9a2a-5072c2d01756",
"fields": {
"Fields.10": "1001",
"Fields.11": "Test1",
"Fields.12": "Fixed1"
}
},
{
"Tranid": "145800f9-c4a5-4625-84d7-29af5e674a14",
"fields": {
"Fields.10": "1002",
"Fields.11": "Test2",
"Fields.12": "Fixed2"
}
}
]
}
But i need the data in following format
{
"data": [
{
"TransactionID": "34540d40-7db8-44c1-9a2a-5072c2d01756",
"fieldsList": [
{
"fieldId": "10",
"fieldValue": "1001"
},
{
"fieldId": "11",
"fieldValue": "Test1"
},
{
"fieldId": "12",
"fieldValue": "Fixed1"
}
]
},
{
"TransactionID": "145800f9-c4a5-4625-84d7-29af5e674a14",
"fieldsList": [
{
"fieldId": "10",
"fieldValue": "1002"
},
{
"fieldId": "11",
"fieldValue": "Test2"
},
{
"fieldId": "12",
"fieldValue": "Fixed2"
}
]
}
]
}
How can i achieve this ? is possible to deserialise using JObject or JArray? Please help.
i have tried to create following model class and tried to deserialise but not getting result as expected.
public class PipeLineResponse
{
public string TransactionID { get; set; }
public List<Dictionary<string, string>> fields { get; set; }
}
public class PipeLineResponseObject
{
public List<PipeLineResponse> data { get; set; }
}
How to create that json in that format any DTO or Automapper will work ? Please help me with samples.
The solution that I am laying down here takes the DTO approach. The response from the service is being deserialized to the DTO, which further is being manually mapped to the final ViewModel that we are sending to the client. By no means, this implementation is production-ready and there is scope for improvement, for which I am adding in comments. But this gives a detailed understanding of how we can handle these kind of scenarios. We are making use of Newtonsoft.Json, which can be pulled into your project via the NuGet package manager.
Structure of the DTO
// RootDTO.cs
// This structure is directly based on the response obtained from remote service.
public class Fields
{
[JsonProperty(PropertyName ="Fields.10")]
public string Fields10 { get; set; }
[JsonProperty(PropertyName = "Fields.11")]
public string Fields11 { get; set; }
[JsonProperty(PropertyName = "Fields.12")]
public string Fields12 { get; set; }
}
public class Datum
{
public string Tranid { get; set; }
public Fields fields { get; set; }
}
public class RootDTO
{
[JsonProperty(PropertyName ="data")]
public List<Datum> data { get; set; }
}
Structure of ViewModel
// PipelineResponse.cs
public class FieldsList
{
public string fieldId { get; set; }
public string fieldValue { get; set; }
}
public class ResponseDatum
{
[JsonProperty(PropertyName = "TransactionID")]
public string TransactionID { get; set; }
public List<FieldsList> fieldsList { get; set; }
}
public class PipelineResponse
{
public List<ResponseDatum> data { get; set; }
}
Deserializing the response to the DTO
// ...other code
var responseString = await httpResponseMessage.Content.ReadAsAsync<Object>();
// This is where the DTO object is created. This should be mapped to view model type.
var responseDTO = JsonConvert.DeserializeObject<RootDTO>(responseString);
Mapping the DTO to ViewModel
The mapping from DTO type to ViewModel type needs to be done before sending the response to the client. It is the view model type that is sent to the client. This logic can be placed within a separate helper (ideally, to separate concerns) or any other location as per the practices you are following.
public PipelineResponse ConvertResponseDTOToResponse(RootDTO responseDTO)
{
// FieldId is being hardcoded here. Instead, you can use Reflection to
// fetch the property name, split on '.' and take the item at index 1.
// Notice that DTO properties have "JsonProperty" attributes for this.
try
{
List<ResponseDatum> responseList = new List<ResponseDatum>();
if (responseDTO != null)
{
// Reflection can be used to avoid hardcoding on 'fieldId'
foreach (var item in responseDTO.data)
{
var responseDataObj = new ResponseDatum
{
TransactionID = item.Tranid,
fieldsList = new List<FieldsList>
{
new FieldsList
{
fieldValue = item.fields.Fields10,
fieldId = "10"
},
new FieldsList
{
fieldValue = item.fields.Fields11,
fieldId = "11"
},
new FieldsList
{
fieldValue = item.fields.Fields12,
fieldId = "12"
}
}
};
responseList.Add(responseDataObj);
}
}
// This object is what you return from your controller endpoint finally.
// The serialized response of this object is of the json structure you need
return new PipelineResponse { data = responseList };
}
catch (Exception ex)
{
throw ex;
}
}

Deserialize JSON array with unknown keys inside JSON object to a generic property - C#

Finding the right title for this problem was kinda hard so I'll try to explain the problem a bit better below.
I am making a call to an API which returns the following JSON object:
{{
"id": "jsonrpc",
"jsonrpc": "2.0",
"result": {
"result": [
{
"AccountId": 285929,
"Flags": [
"Managed_Obsolete"
],
"PartnerId": 73560,
"Settings": [
{
"AN": "company_1"
},
{
"CD": "1435323320"
},
{
"ED": "2147483647"
},
{
"OS": "Windows Server 2012 R2 Standard Edition (9600), 64-bit"
},
{
"OT": "2"
},
{
"T3": "1085792125772"
},
{
"US": "958222150780"
},
{
"YS": "100"
}
]
},
{
"AccountId": 610474,
"Flags": null,
"PartnerId": 249262,
"Settings": [
{
"AN": "company_2"
},
{
"CD": "1522143635"
},
{
"ED": "2147483647"
},
{
"OS": "Windows 7 Professional Service Pack 1 (7601), 64-bit"
},
{
"OT": "2"
},
{
"T3": "598346102236"
},
{
"US": "758149148249"
},
{
"YS": "100"
}
]
},
],
"totalStatistics": null
},
}}
In above result I listed only the first 2 accounts (total of 80+ accounts normally).
Deserializing the object works fine, I am putting the JSON object fields inside my C# model (list).
The problem however is that I can't get the (inner) Settings array properly in my model. The settings array keys are unknown, I define these keys when I call the API:
JObject requestObject = new JObject();
requestObject.Add(new JProperty("id", "jsonrpc"));
requestObject.Add(new JProperty("jsonrpc", "2.0"));
requestObject.Add(new JProperty("method", "myMethod"));
requestObject.Add(new JProperty("visa", someID));
requestObject.Add(new JProperty("params",
new JObject(
new JProperty("query", new JObject(
new JProperty("PartnerId", partnerId),
new JProperty("StartRecordNumber", 0),
new JProperty("RecordsCount", 9999999),
new JProperty("Columns", new JArray("AR", "AN", "US", "T3", "OT", "OS", "YS"))
)),
new JProperty("timeslice", unixDate),
new JProperty("totalStatistics", "*")
))
);
In above call I define the keys for the Settings array, this could however also be just one key or more. For this reason I want to make my Settings property in my C# model generic (I don't want to list all the possible key names because this are over 100 keys).
What I had so far:
List<EnumerateAccountHistoryStatisticsResult> resultList = new List<EnumerateAccountHistoryStatisticsResult>();
var result = JsonConvert.DeserializeObject<JObject>(streamreader.ReadToEnd());
dynamic innerResult = result["result"]["result"];
foreach (var obj in innerResult)
{
resultList.Add(
new EnumerateAccountHistoryStatisticsResult
{
AccountId = obj.AccountId,
Flags = obj.Flags.ToObject<IEnumerable<string>>(),
PartnerId = obj.PartnerId,
Settings = obj.Settings.ToObject<List<ColumnSettingsResult>>(),
});
}
The EnumerateAccountHistoryStatisticsResult Model:
public class EnumerateAccountHistoryStatisticsResult
{
public int AccountId { get; set; }
public IEnumerable<string> Flags { get; set; }
public int PartnerId { get; set; }
public List<ColumnSettingsResult> Settings { get; set; }
}
The ColumnSettingsResult model:
public class ColumnSettingsResult
{
public string AR { get; set; }
public string AN { get; set; }
public string US { get; set; }
public string T3 { get; set; }
public string OT { get; set; }
public string OS { get; set; }
public string YS { get; set; }
// and list all other columns...
}
With above models I would need to list all the possible columns which are over 100 properties, besides that the result of the Settings list is not logical because I get all the property values but for each different key I get null values:
The ColumnSettingsResult model should more be something like:
public class ColumnSettingsResult
{
public string ColumnName { get; set; }
public string ColumnValue { get; set; }
}
I cant get the key and value inside these two properties though without defining the key name inside the model..
I already tried several things without result (links below as reference).
Anyone that can get me in the right direction?
C# deserialize Json unknown keys
Convert JObject into Dictionary<string, object>. Is it possible?
Convert Newtonsoft.Json.Linq.JArray to a list of specific object type
Try making Settings of type Dictionary<string,string> (or List<KeyValuePair<string,string>> if Dictionary doesn't give you what you want.
public class MyJsonObject
{
public string id { get; set; }
public string jsonrpc { get; set; }
public Result result { get; set; }
public class Result2
{
public int AccountId { get; set; }
public List<string> Flags { get; set; }
public int PartnerId { get; set; }
public Dictionary<string,string> Settings { get; set; } //or List<KeyValuePair<string,string>>
}
public class Result
{
public List<Result2> result { get; set; }
public object totalStatistics { get; set; }
}
}
Then JsonConvert.DerserializeObject<MyJsonObject>(jsonString);

C# model for nested properties

I need to make API request to CloudFlare to purge cache of individual files.
Can someone please guide how to represent the below as C# model class.
files: [
"http://www.example.com/css/styles.css",
{
"url": "http://www.example.com/cat_picture.jpg",
"headers": {
"Origin": "cloudflare.com",
"CF-IPCountry": "US",
"CF-Device-Type": "desktop"
}
}
]
var obj = new
{
files = new object[]
{
"http://www.example.com/css/styles.css",
new
{
url = "http://www.example.com/cat_picture.jpg",
headers = new Dictionary<string,string>
{
{ "Origin", "cloudflare.com" },
{ "CF-IPCountry","US" },
{ "CF-Device-Type", "desktop"}
}
}
}
};
The dictionary is there because of the awkward property names like CF-IPCountry which make it not possible to use an anonymous type.
To show it working:
var json = Newtonsoft.Json.JsonConvert.SerializeObject(obj, Formatting.Indented);
gives:
{
"files": [
"http://www.example.com/css/styles.css",
{
"url": "http://www.example.com/cat_picture.jpg",
"headers": {
"Origin": "cloudflare.com",
"CF-IPCountry": "US",
"CF-Device-Type": "desktop"
}
}
]
}
Edit; that's not quite right - the dictionary didn't work, but I don't have time to fix it.
Using classes (maybe you could use better class names then mine :) ):
class Root
{
[JsonProperty(PropertyName = "files")]
public List<object> Files { get; set; } = new List<object>();
}
class UrlContainer
{
[JsonProperty(PropertyName = "url")]
public string Url { get; set; }
[JsonProperty(PropertyName = "headers")]
public Headers Headers { get; set; }
}
class Headers
{
public string Origin { get; set; }
[JsonProperty(PropertyName = "CF-IPCountry")]
public string CfIpCountry { get; set; }
[JsonProperty(PropertyName = "CF-Device-Type")]
public string CfDeviceType { get; set; }
}
Usage
var root = new Root();
// Add a simple url string
root.Files.Add("http://www.example.com/css/styles.css");
var urlContainer = new UrlContainer() {
Url = "http://www.example.com/cat_picture.jpg",
Headers = new Headers()
{
Origin = "cloudflare.com",
CfIpCountry = "US",
CfDeviceType = "desktop"
}
};
// Adding url with headers
root.Files.Add(urlContainer);
string json = JsonConvert.SerializeObject(root);
Note: I'm using Newtonsoft.Json

Deserializing the JSON in the C# console application

I want to deserialize the JSON response from the web service call in the C# Console application.JSON looks like
{
"il_response": {
"custom_forms": [
{
"id": 84,
"name": "Request",
"note": "Notes",
"fields": [
{
"label": "Prj",
"value": [
"ABC",
"DEF"
],
"name": "Projects"
},
{
"label": "Submit Date",
"value": "October 16, 2017 ",
"name": "Submit Date"
},
{
"label": "Name",
"value": "Dhana",
"name": "Name"
}
]
}
],
"il_metadata": {}
}
}
I have all the POCO in the seperate class file called iDTO.cs
public class iResponse
{
public iResponse(){ }
public List<iDTO> data { get; set; }
}
public class iDTO
{
public iDTO() { }
}
public class Field
{
public string label { get; set; }
public object value { get; set; }
public string name { get; set; }
}
public class C_Form
{
public int id { get; set; }
public string name { get; set; }
public string note { get; set; }
public List<Field> fields { get; set; }
}
public class IlMetadata
{
}
public class IlResponse
{
public List<C_Form> c_forms { get; set; }
public IlMetadata il_metadata { get; set; }
}
public class RootObject
{
public IlResponse il_response { get; set; }
}
Below is my code where I am calling the service
public class APICall
{
string BaseUR = ConfigurationManager.AppSettings["BaseURL"];
string accessToken = ConfigurationManager.AppSettings["AccessToken"];
public async Task<IHttpActionResult> Get()
{
using (var client = new HttpClient())
{
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; });
Uri uri = new Uri(BaseURL);
client.BaseAddress = uri;
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken);
var response = await client.GetAsync(uri);
var datafile = await response.Content.ReadAsStringAsync();
var returnDataObj = JsonConvert.DeserializeObject<iLabDTO.RootObject>(datafile);
}
}
}
Here I am not sure how to get each name and value property from the field in the JSON. I see that we can use the for each with the fields but not surehow to get both the value and name
This worked using your json, after amending the problem mentioned by Luke. However I'm using iDTO.RootObject whereas your api says iLabDTO.RootObject.
string datafile = File.ReadAllText(#"json.txt");
var returnDataObj = JsonConvert.DeserializeObject<iDTO.RootObject>(datafile);
foreach (var form in returnDataObj.il_response.c_forms)
{
Console.WriteLine(form.id);
Console.WriteLine(form.name);
Console.WriteLine(form.note);
foreach(var field in form.fields)
{
Console.WriteLine(field.name);
Console.WriteLine(field.label);
Console.WriteLine(field.value);
}
}
The c_forms property on your IlResponse object will need to have the same name as the corresponding property on your JSON object:
{
"il_response": {
"custom_forms": [
From the above code snippet, you should rename the c_forms property to custom_forms instead.
That's the only occurrance of that issue that I see, but you will need to make sure that all of your C# property names match up with the JSON object names that are being received. This will then give you the property name, as it is in your C# model object, and the value, which will be held inside of the named property at runtime.

Newtonsoft JSON Deserialize AND jsonfreeze

I have a two simple PHP class
class Order{
public $orderNo;
public $lines = array();
public $paid = false;
public function addLine(OrderLine $line) {
$this->lines[] = $line;
}
public function setPaid($paid = true) {
$this->paid = true;
}
}
class OrderLine{
public function __construct($item, $amount){
$this->item = $item;
$this->amount = $amount;
}
public $item;
public $amount;
public $options;
}
Serialize object uses https://github.com/mindplay-dk/jsonfreeze
...
$json = new JsonSerializer;
$data = $json->serialize($order);
Have output:
{
"#type": "Order",
"orderNo": 123,
"lines": [{
"#type": "OrderLine",
"item": "milk \"fuzz\"",
"amount": 3,
"options": null
},{
"#type": "OrderLine",
"item": "cookies",
"amount": 7,
"options": {
"#type": "#hash",
"flavor": "chocolate",
"weight": "1\/2 lb"
}
}],
"paid": true
}
Send the string XMLRPC in VB.NET
As using Newtonsoft JSON get a live object?
As well as how to create a compatible format by analogy with the json string of living VB.net OR C# object?
Here's something you could start with. You create some classes with properties which represent the JSON Format (untested code, just as idea):
public class MyData
{
[JsonProperty("#type")]
public string Type { get; set; }
[JsonProperty("#orderNo")]
public int OrderNo { get; set;
[JsonProperty("paid")]
public bool Paid { get; set; }
[JsonProperty("lines")]
public List<MyDataLine> Lines { get; set; }
}
public class MyDataLines
{
[JsonProperty("#type")]
public string Type { get; set; }
[JsonProperty("options")]
public MyDataLinesOptions Options { get; set; }
// ... more
}
public class MyDataLinesOptions
{
// ... more
}
Then you can serialize and deserialize the the data like this:
string json = "the json data you received";
MyData myData = JsonConvert.DeserializeObject<MyData>(json);
// ...
json = JsonConvert.SerializeObject(myData);
"#type": "Order"
and
"#type": "OrderLine",
this is not a property, this is an indication of the type of the object

Categories