Deserializing the JSON object passed from Ajax to ASP.NET MVC - c#

I'm trying to send the JSON object through Ajax request to ASP.NET MVC. While getting the JSON object in the backend, it receives the object in string format as shown below.
How to deserialize it into a C# object?
C# ASP.NET MVC code:
public string GetLists(string list1)
{
return JsonConvert.SerializeObject(list1, JsonSetting);
}
JavaScript code:
button.onclick=()=> {
let xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
if (xhr.readyState == 4 && xhr.status == 200) {
console.log(xhr.response)
}
}
let list1=[
{"id":"1a","level":1},
{"id":"2b","level":14},
{"id":"3c","level":23}
]
var params ="list1=" + JSON.stringify(list1)
xhr.open("POST", "/Status/GetLists");
xhr.setRequestHeader('content-type', "application/x-www-form-urlencoded");
xhr.send(params);
}
Output:
[
{\"id\":\"1a\",\"level\":1},
{\"id\":\"2b\",\"level\":14},
{\"id\":\"3c\",\"level\":23}
]

You can use System.Text.Json.JsonSerializer
but you should include a type for deserialization so that c# knows the kind of data you are trying to receive.
In your code, you only give back the string but not a c# object so you have to specify a class or an interface to model your data.
Example snippet for receiving the data:
using System;
using System.Collections.Generic;
using System.Text.Json;
// define the structure of the object you want to recieve
interface MyDataStructure
{
public string id { get; set; }
public int level { get; set; }
}
class Program
{
public void ReadData(string jsonData)
{
// Creating a list of objects from your type. The Json Serializer is flexible and also workes when using Arrays
List<MyDataStructure> data = JsonSerializer.Deserialize<List<MyDataStructure>>(jsonData);
foreach (var obj in data){
Console.WriteLine($"{obj.id}||{obj.level}");
}
}
}
I recommend you should check out the docs on deserialization and serialization.
An alternative to the .Net JsonSerializer is Newtonsofts Json.NET

your data is serialized twice, at first you serialize it manually , after this net serialized it automaticaly to send data back. So you don't need to serialize it at all, net will do it for you. Change your action to this
public ActionResult<List<Item>> GetLists([FromBody] List<item> list)
{
return Ok(list);
}
class
public class Item
{
public string id { get; set; }
public string level { get; set; }
}
and ajax
var params =JSON.stringify(list1);
xhr.open("POST", "/Status/GetLists");
xhr.setRequestHeader('content-type', "application/json");
xhr.send(params);

You are trying to send a json object with a wrong content-type, in your case the content-type should be application/json and you don't have to add "params" + JSON.stringify(list1) as this will be sent in the request body, you should send just the list1 variable.
xhr.setRequestHeader('content-type', "application/json");
xhr.send(JSON.stringify(list1));
Then you will be able to deserialize the object in your backend endpoint like:
public class Item
{
public string id { get; set; }
public string level { get; set; }
}
[HttpPost]
public List<Item> GetLists([FromBody] object value)
{
var items = JsonConvert.DeserializeObject<List<Item>>(value.ToString());
return items;
}

Related

How to fix error Cannot deserialize the current JSON object? [duplicate]

Below is a (slightly) stripped down response I get from a REST API upon successful creation of a new "job code" entry. I need to deserialize the response into some classes, but I'm stumped.
For reference, I'm using JSON.NET in .NET 3.5 (running in a SSIS script in SQL Server 2008 R2) to attempt my deserialization. Here's the JSON - which I obviously have no control over as it's coming from someone else's API:
{
"results":{
"jobcodes":{
"1":{
"_status_code":200,
"_status_message":"Created",
"id":444444444,
"assigned_to_all":false,
"billable":true,
"active":true,
"type":"regular",
"name":"1234 Main Street - Jackson"
},
"2":{
"_status_code":200,
"_status_message":"Created",
"id":1234567890,
"assigned_to_all":false,
"billable":true,
"active":true,
"type":"regular",
"name":"4321 Some Other Street - Jackson"
}
}
}
}
In my C# code, I do have a "JobCode" class defined which only partially maps the JSON values to properties - I'm not interested in all of the data that's returned to me:
[JsonObject]
class JobCode
{
[JsonProperty("_status_code")]
public string StatusCode { get; set; }
[JsonProperty("_status_message")]
public string StatusMessage { get; set; }
[JsonProperty("id")]
public string Id {get; set;}
[JsonProperty("name")]
public string Name { get; set; }
//-------------------------------------------------------------------------------
// Empty constructor for JSON serialization support
//-------------------------------------------------------------------------------
public JobCode() { }
}
I'm attempting to deserialize the data via this call:
newResource = JsonConvert.DeserializeObject<JobCode>(jsonResponse);
Where jsonResponse is the code outputted above.
When I execute the code, "newResource" always comes back as null - which is not unexpected because I know that there are actually multiple jobcodes in the data and this code is trying to deserialize it into a single JobCode object. I tried creating a new class called "JobCodes" that looks like this:
class JobCodes
{
[JsonProperty("jobcodes")]
public List<JobCode>_JobCodes { get; set; }
}
And then I tried calling this:
newResource = JsonConvert.DeserializeObject<JobCodes>(jsonResponse);
But the issue persists - my return object is null.
What's throwing me off, I think, is the presence of the "1" and "2" identifiers. I don't know how to account for their presence in my object design and/or usage of the JSON.NET class / property attributes like [JsonObject],[JsonProperty], etc.
When I run the JSON data through JSON2CSharp, it constructs some weird-looking classes, so that hasn't proven too effective. I've validated the JSON with several different validators and it all checks out - I just don't know what I'm missing here.
Ultimately, I'd like to return a List from the JSON data, but I'm stumped on what I need to do to make that happen.
Your problem is twofold:
You don't have a class defined at the root level. The class structure needs to match the entire JSON, you can't just deserialize from the middle.
Whenever you have an object whose keys can change, you need to use a Dictionary<string, T>. A regular class won't work for that; neither will a List<T>.
Make your classes like this:
class RootObject
{
[JsonProperty("results")]
public Results Results { get; set; }
}
class Results
{
[JsonProperty("jobcodes")]
public Dictionary<string, JobCode> JobCodes { get; set; }
}
class JobCode
{
[JsonProperty("_status_code")]
public string StatusCode { get; set; }
[JsonProperty("_status_message")]
public string StatusMessage { get; set; }
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
}
Then, deserialize like this:
RootObject obj = JsonConvert.DeserializeObject<RootObject>(json);
Working demo here
Excellent Answers!
For those out there that may need some more help with the JSON Class Configuration, try: http://json2csharp.com/#
An excellent way of Auto Generating the Classes!
Or even easier, in VS, Goto:
Edit -> Paste Special -> Paste as JSON Classes
Because you can't change the scheme of JSON, and you can't set constant No. of properties, I'd suggest you to use JObject
var jobject = JObject.Parse(json);
var results = jobject["results"];
var jobcodes = results["jobcodes"];
var output = jobcodes.Children<JProperty>()
.Select(prop => prop.Value.ToObject<JobCode>())
.ToList();
Warning: code assumes, that JSON is always in proper schema. You should also handle invalid schema (for example where property is not of JobCode scheme).
You can also deserialize your json to an object of your target class, and then read its properties as per normal:
var obj = DeSerializeFromStrToObj<ClassToSerialize>(jsonStr);
Console.WriteLine($"Property: {obj.Property}");
where DeSerializeFromStrToObj is a custom class that makes use of reflection to instantiate an object of a targeted class:
public static T DeSerializeFromStrToObj<T>(string json)
{
try
{
var o = (T)Activator.CreateInstance(typeof(T));
try
{
var jsonDict = JsonSerializer.Deserialize<Dictionary<string, string>>(json);
var props = o.GetType().GetProperties();
if (props == null || props.Length == 0)
{
Debug.WriteLine($"Error: properties from target class '{typeof(T)}' could not be read using reflection");
return default;
}
if (jsonDict.Count != props.Length)
{
Debug.WriteLine($"Error: number of json lines ({jsonDict.Count}) should be the same as number of properties ({props.Length})of our class '{typeof(T)}'");
return default;
}
foreach (var prop in props)
{
if (prop == null)
{
Debug.WriteLine($"Error: there was a prop='null' in our target class '{typeof(T)}'");
return default;
}
if (!jsonDict.ContainsKey(prop.Name))
{
Debug.WriteLine($"Error: jsonStr does not refer to target class '{typeof(T)}'");
return default;
}
var value = jsonDict[prop.Name];
Type t = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType;
object safeValue = value ?? Convert.ChangeType(value, t);
prop.SetValue(o, safeValue, null); // initialize property
}
return o;
}
catch (Exception e2)
{
Debug.WriteLine(e2.Message);
return o;
}
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
return default;
}
}
A complete working example class can be found in my enhanced answer to a similar question, here

Angular 8 post formData to ASP.NET Core API cannot bind IEnumerable/List

As title says
TypeScript model
export interface RigheOrdiniBuoniSpesaData {
id: number;
id_ordine: number;
id_taglio_buono_spesa: number;
quantita: number;
}
which is part of another bigger object:
export class OrdiniBuoniSpesaData {
id: number;
// OTHER FIELD
// OTHER FIELD
// OTHER FIELD
righe_ordine: RigheOrdiniBuoniSpesaTableData;
}
Save method
saveOrder(model: OrdiniBuoniSpesaData) {
const headerPost: HttpHeaders = new HttpHeaders();
headerPost.set('Content-type', 'application/x-www-form-urlencoded');
const formData: FormData = new FormData();
formData.append('id_cliente', model.id_cliente.toString());
// VARIOUS FORM FIELDS
//THIS IS ARRAY DATA
formData.append('righe_ordine', JSON.stringify(model.righe_ordine));
return this.http
.post<boolean>(
requestURL,
formData,
{ headers: headerPost }
)
.pipe(
catchError(this.handleError)
);
}
Order json (valid Json) is visible in Chrome request capture clearly along with all data:
[{"id":0,"id_ordine":0,"id_taglio_buono_spesa":1,"quantita":1},{"id":0,"id_ordine":0,"id_taglio_buono_spesa":1,"quantita":1},{"id":0,"id_ordine":0,"id_taglio_buono_spesa":1,"quantita":1},{"id":0,"id_ordine":0,"id_taglio_buono_spesa":3,"quantita":14}]
On API Side
Receiving model for JSON
public class RigheOrdiniBuoniSpesaViewModel
{
public long id { get; set; }
public long id_ordine { get; set; }
public long id_taglio_buono_spesa { get; set; }
public int quantita { get; set; }
}
Which is in
public class OrdiniBuoniSpesaViewModel
{
public long id { get; set; }
//OTHER VARIOUS FIELDS
//I TRIED ALSO WITH LIST INSTEAD OF IENUMERABLE
public IEnumerable<RigheOrdiniBuoniSpesaViewModel> righe_ordine {get;set;}
}
(I TRIED ALSO WITH LIST INSTEAD OF IENUMERABLE, STILL NO LUCK!)
Api controller signature:
[HttpPost]
public async Task<IActionResult> PostSaveOrder([FromForm] OrdiniBuoniSpesaViewModel model)
{.....code}
All the fields are binded correctly except for the righe_ordine array!
I can see every field correctly but the array has count = 0.
Strangely enough, if I examine the asp net request object (this.Request.Form) in the QuickWatch debug in visual studio:
this.Request.Form["righe_ordine"]
{[{"id":0,"id_ordine":0,"id_taglio_buono_spesa":1,"quantita":1},
{"id":0,"id_ordine":0,"id_taglio_buono_spesa":1,"quantita":1},
{"id":0,"id_ordine":0,"id_taglio_buono_spesa":1,"quantita":1},
{"id":0,"id_ordine":0,"id_taglio_buono_spesa":3,"quantita":14}]}
Microsoft.Extensions.Primitives.StringValues
is present and correctly populated..... but for some reason binding it to the OrdiniBuoniSpesaViewModel fails....
What am I doing wrong? Any idea?
EDIT:
For the moment the only solution I found is to directly catch the value just after entering the controller:
string righeJson = this.Request.Form["righe_ordine"].ToString();
if(!string.IsNullOrEmpty(righeJson))
model.righe_ordine = JsonConvert.DeserializeObject<IEnumerable<RigheOrdiniBuoniSpesaViewModel>>(righeJson);
I think you need to change your model name like this
In your js code
formData.append('righeOrdine', JSON.stringify(model.righe_ordine));
and c#
public class OrdiniBuoniSpesaViewModel
{
public long id { get; set; }
//OTHER VARIOUS FIELDS
//I TRIED ALSO WITH LIST INSTEAD OF IENUMERABLE
public IEnumerable<RigheOrdiniBuoniSpesaViewModel> RigheOrdine {get;set;}
}
Update: Just for enhancement
Maybe you need to parse it before using it in your controller
You can read my full code here
We will need to create interface and class to implement it.
public interface IJsonParseService<T> where T : class
{
T ToObject(string json);
}
public class JsonParseService<T> : IJsonParseService<T> where T : class
{
public T ToObject(string json)
{
return JObject.Parse(json).Root.ToObject<T>();
}
}
Then register it
services.AddScoped(typeof(IJsonParseService<>), typeof(JsonParseService<>));
So your code should be something like this
private readonly IJsonParseService<RigheOrdiniBuoniSpesaViewModel> _jsonParsePostOptionDefaultVm;
jsonParsePostOptionDefaultVm.ToObject(viewModel.RigheOrdiniBuoniSpesaViewModel);

C# consume Angular 4 class

hou would i consume the following request in my C# web Api.
Here is my Request:
this._http.post(
this.url + 'api/Test',
{ auth: this.authClass},
{ headers: this.header }
).map(
(res) => res.json()
).subscribe(
(res) => {
console.log("VALUE RECEIVED: ", res);
})
Here is my AuthClass:
export class Auth{
username:string = "";
password:string = "";
}
I have the same class in c# and need to know how do i go about receiving the class as a whole in c#.
I tried:
[HttpPost]
public IHttpActionResult login(HttpRequestMessage request)
{
var jsonString = await request.Content.ReadAsStringAsync();
var model = JsonConvert.DeserializeObject<auth>(jsonString);
return Ok();
}
My problem is that it can't parse the json to auth. My json string getting send currently looks like this.
{
"auth": {
"username": "admin",
"password": "admin1"
}
}
If its just
{
"username": "admin",
"password": "admin1"
}
then it works without any problems. But i need it to consume the first json example
about receiving the class as a whole in c#.
You wouldn't get the class. The recommended wire transfer is JSON and it will just give you the Object properties.
More
Lookup JSON serialization. The internet is full of examples with C# + AngularJS.
Arno,
You should receive a json object in your web api method.
Then use Json.Net to convert your json object to the C# object you desire.
Hope it helps.
So the answer was to wrap the auth class inside another class.
public class authReq
{
public auth request { get; set; }
}
public class auth
{
public string username { get; set; }
public string password { get; set; }
}
And then i deserialize the authReq like so.
var model = JsonConvert.DeserializeObject<register>(jsonString);

Strange JSON Deserialization c#

I'm having troubles with some Deserialization because I'm not receiving an object like I'm used to but an array. Lets to the code, how I've being doing it:
JSON received:
[{"gcm_regID":"fsdf2156fw62f1wfwe512g"}]
Deserialization
var result = JsonConvert.DeserializeObject<Gcm>(response);
Handlers Tried:
public class Gcm
{
public List<Gcm_reg> gcm_regID { get; set; }
}
public class Gcm_reg
{
public string gcm_regID { get; set; }
}
I've tried just the Gcm_reg as well and nothing seems to work. How can I deserialize this? By the way, I'm using the newsoft JSON.
Best regards,
IEnumerable<Gcm_reg> result = JsonConvert.DeserializeObject<IEnumerable<Gcm_reg>>(response);
You can deserialize the object as an array or IEnumerable
Object should be like
public class Gcm
{
public string gcm_regID { get; set; }
}
So Try it
var result = JsonConvert.DeserializeObject<IEnumerable<Gcm>>(response);
You can directly generate classes from Json by using link
To create same type of object
var outputList = new List<Gcm>();
//loop through this part based on number of input
var itemToAdd = new Gcm();
itemToAdd .regID = 'something';
outputList.Add(itemToAdd );
and send outputList to server.

JSON.Net in Api controllers w/ param containing Dictionary is always null

I've seen some tutorials out there that claim to work, but they are outdated or simply do not work.
How can I use JSON.Net to serialize and deserialize the data received to and sent from my API controllers?
We are using VS2012.
Update
I have a model like this
public class SearchModel
{
public int PageIndex { get; set; }
public int PageSize { get; set; }
public Dictionary<string, object> Terms { get; set; }
}
And an Api controller like this
public class ModelSearchApiController : ApiController
{
public List<Model> Get([FromUri] SearchModel search)
{
return new List<Model>();
}
}
However, search provides the correct value set in the Ajax request, the property Terms is always an empty dictionary.
I know we can provide a value like [ { Key:"foo", Value:123 } ] but why can't I just pass a normal JSON object (ie { foo:123 }) ??? Why can it serialize a Dictionary into a nice standard JSON object, but cannot take that exact same object and recreate a Dictionary. This is beyound me.
Edit
In other words, if the browser sends these arguments :
pageIndex: 0
pageSize: 100
terms[foo]: Bar
terms[buz]: 1234
What would be the required object signature? Because the object mentionned above does not work and the dictionary is just empty.
JSON.NET is the default serializer for ASP.NET Web API - it can convert between JSON and CLR objects, and does so for all JSON input. However, you're not trying to convert a JSON input to your SearchModel - you're trying to convert from the URI-based format which is similar to application/x-www-form-urlencoded, into the CLR type SearchModel, and that is not supported by JSON.NET (it's not JSON!). In general, the serializers are used to convert (on incoming requests) from the request body to the action parameter.
Let's look at this (complete) example below (assuming the default route, to "api/{controller}"). It's very similar to your question, but I also added a Post method in addition to the GET method.
public class ModelSearchApiController : ApiController
{
public List<Model> Get([FromUri] SearchModel search)
{
return new List<Model>
{
new Model { PageIndex = search.PageIndex, PageSize = search.PageSize, Terms = search.Terms }
};
}
public List<Model> Post(SearchModel search)
{
return new List<Model>
{
new Model { PageIndex = search.PageIndex, PageSize = search.PageSize, Terms = search.Terms }
};
}
}
public class Model
{
public int PageIndex { get; set; }
public int PageSize { get; set; }
public Dictionary<string, object> Terms { get; set; }
}
public class SearchModel
{
public int PageIndex { get; set; }
public int PageSize { get; set; }
public Dictionary<string, object> Terms { get; set; }
}
If you send this request to the server:
POST http://localhost:64699/api/ModelSearchApi HTTP/1.1
User-Agent: Fiddler
Host: localhost:64699
Content-Type: application/json
Content-Length: 65
{"PageIndex":1,"PageSize":10,"Terms":{"foo":"bar","foo2":"bar2"}}
It will be bound, as you expect, to the SearchModel parameter - the Terms property will be a dictionary with two entries (foo=bar, foo2=bar2).
Now, for the GET parameter. ASP.NET Web API has a concept of model binders and value provider, which would be the component which would convert between the query string into the action parameters. The default binder / provider do not support the "arbitrary" name/value pair syntax *for dictionary inside complex types. You can, as you pointed out, use the key/value pair syntax, and that will be understood, as shown below.
GET http://localhost:64699/api/ModelSearchApi?PageIndex=1&PageSize=10&Terms[0][key]=foo&Terms[0][value]=bar HTTP/1.1
User-Agent: Fiddler
Host: localhost:64699
Now, for your problem you have two options. You can change your API to use a custom model binder or value provider which knows how to understand the "simple" name/value syntax, as shown below:
public class ModelSearchApiController : ApiController
{
public List<Model> Get([ModelBinder(typeof(MySearchModelBinder))] SearchModel search)
{
return new List<Model>
{
new Model { PageIndex = search.PageIndex, PageSize = search.PageSize, Terms = search.Terms }
};
}
}
public class MySearchModelBinder : IModelBinder
{
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
SearchModel value = new SearchModel();
value.Terms = new Dictionary<string,object>();
foreach (var queryParams in actionContext.Request.GetQueryNameValuePairs())
{
if (queryParams.Key == "PageIndex")
{
value.PageIndex = int.Parse(queryParams.Value);
}
else if (queryParams.Key == "PageSize")
{
value.PageSize = int.Parse(queryParams.Value);
}
else if (queryParams.Key.StartsWith("Terms."))
{
value.Terms.Add(queryParams.Key.Substring("Terms.".Length), queryParams.Value);
}
}
bindingContext.Model = value;
return true;
}
}
Another option is to pre-process your input data on the client prior to sending to the server, using a function similar to the one below.
function objToKVPArray(obj) {
var result = [];
var k;
for (k in obj) {
if (obj.hasOwnProperty(k)) {
result.push({ key: k, value: obj[k] });
}
}
return result;
}
You can take reference the link below. Hope this help.
And here is sample using Json.net with web API.

Categories

Resources