I have an ASP.NET Core web app that works fine, when using this controller implementation:
[HttpPost]
public ActionResult SetAssignment()
{
string b = "";
using (StreamReader rdr = new StreamReader(Request.Body)) b = rdr.ReadToEnd();
Assignment asg = JsonConvert.DeserializeObject<Assignment>(b);
// asg is valid here
// ... do other stuff ...
}
But it does NOT work when using this implementation:
[HttpPost]
public ActionResult SetAssignment([FromBody] Assignment asg)
{
// asg is always NULL here
// ... do other stuff...
}
Theoretically, these implementations should be identical (although the second would be way more elegant), no?
I seem to be missing something...
The way this method is called looks like this, btw:
function asgupdate() {
var asp = {};
asp.AssignmentId = $("#asp_idx").val();
...
$.ajax({
type: "POST",
url: "/Project/SetAssignment",
data: JSON.stringify(asp),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (r) {
...
}
});
};
The JSON request does get sent by the browser and it look correct (afaict).
The assignment model looks like this:
public class Assignment
{
[Key]
public int AssignmentId { get; set; }
[Required]
public int CrewMemberId { get; set; }
[Required]
public int ProjectId { get; set; }
[DisplayName("Function")]
public string Function { get; set; } = "";
public string? Account { get; set; }
public float Rate { get; set; } = 0;
public float PrepWeeks { get; set; } = 0;
public float ShootWeeks { get; set; } = 0;
public float WrapWeeks { get; set; } = 0;
public float PostWeeks { get; set; } = 0;
public DateTime Created { get; set; } = DateTime.UtcNow;
public DateTime Updated { get; set; } = DateTime.UtcNow;
public AssignmentTypes? AssignmentType { get; set; } = AdditionalClasses.AssignmentTypes.SALARYNORMAL;
public virtual CrewMember CrewMember { get; set; }
public virtual Project? Project { get; set; }
}
Ok, the solution was a mix of several things, thanks to #dbc for pointing me in the right direction...
I wrongly assumed that the automatic JSON conversion would be identical to Newtonsoft.JSON, which it is definitely NOT.
Since System.Text.Json is more strict in how it interprets stuff, I had to add some options:
builder.Services.AddControllers().AddJsonOptions(x =>
{
x.JsonSerializerOptions.NumberHandling = JsonNumberHandling.AllowReadingFromString;
x.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
});
Since Enums are also treated differently (the NumberHandling options does NOT apply to Enum parsing, I had to change the generating JavaScript to always send an integer instead of a string. I tried to use JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()), however that broke the sending back of the enum (because it would now send the string name of the enum instead of the integer value back to the webfrontend, which couldn't deal with it).
I'm developing an api in net core.
I've done a post function in which I send an object containing multiple parameters and a list within another list.
When I'm debugging the code the function is called correctly but I find that the second list always arrives null.
The rest of the data arrives at you correctly. I have done different tests with other objects and everything works correctly.
It is this case in which the list within another the second one arrives null.
My code:
example request input
{
"Name": "TestName",
"Related1":
[{
"id1": "TestNameRelated1",
"Related2":
[{
"id2": "TestNameRelated2"
}]
}]
}
[HttpPost]
public resultExample Test([FromBody]TestClass test)
{
//do something
}
[DataContract]
public class TestClass
{
[DataMember]
public string Name { get; set; }
[DataMember]
public List<TestClassArray> Related1 { get; set; }
}
[DataContract]
public class TestClassArray
{
[DataMember]
public string id1 { get; set; }
[DataMember]
public List<TestClassArray2> Related2 { get; set; }
}
[DataContract]
public class TestClassArray2
{
[DataMember]
public string id2 { get; set; }
}
This api was previously made in .NET framework 4.8 and this case worked correctly.
Now I'm passing the api to .Net5.
Could it be that in .Net5 it is not allowed to pass lists within other lists?
Do you have to enable some kind of configuration to be able to do this now?
You need use class/DTO with constructor like shown below and you should be good to go. I have uploaded this sample API app's code working with .net5.0 on my GitHub here.
public class TestClass
{
public TestClass()
{
Related1 = new List<TestClassArray>();
}
public string Name { get; set; }
public List<TestClassArray> Related1 { get; set; }
}
public class TestClassArray
{
public TestClassArray()
{
Related2 = new List<TestClassArray2>();
}
public string id1 { get; set; }
public List<TestClassArray2> Related2 { get; set; }
}
public class TestClassArray2
{
public string id2 { get; set; }
}
public class ResultExample
{
public string StatusCode { get; set; }
public string Message { get; set; }
}
Controller Post Method
[HttpPost]
[ProducesResponseType(typeof(ResultExample), 200)]
public ResultExample Post([FromBody] TestClass test)
{
ResultExample testResult = new ResultExample();
TestClass test2 = new TestClass();
TestClassArray testClassArray = new TestClassArray();
TestClassArray2 testClassArray2 = new TestClassArray2();
test2.Name = test.Name;
foreach (var item in test.Related1)
{
foreach (var item2 in item.Related2)
{
testClassArray2.id2 = item2.id2;
}
testClassArray.Related2.Add(testClassArray2);
}
test2.Related1.Add(testClassArray);
Console.WriteLine(test2);
testResult.Message = "New Result added successfullly....";
testResult.StatusCode = "201";
return testResult;
}
Swagger Input Sample Payload
Post Controller Result
Response of Sample input payload,(You can change it to default 201 response code as well)
I had a similar issue.
API method shows List was null
In my case a date field was not well formatted
So I use SimpleDateFormat on Android Studio with a correct datetime format
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss",Locale.US);
item.setDate(dateFormat.format(calendar.getTime()));
and works fine
[Update: This question is different from the suggested duplicate because this one is about deserialization of XML and the explanation of the problem and solution on this one is clearer as I've included the full source code.]
I'm trying to read and subsequently manipulate a response from a Web API. Its response looks like this:
<MYAPI xsi:noNamespaceSchemaLocation="MYAPI.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<MySite Resource="some resource name">
<Name>some name</Name>
<URL>some url</URL>
<SecondName>Describes something</SecondName>
</MySite>
... A lot of these <MySite>...</MySite> are there
<SomeOtherSite Resource="some resource name">
<Name>some name</Name>
<URL>some url</URL>
</SomeOtherSite>
</MYAPI>
SomeOtherSite is not repeating and only one of it appears at the end of the response. But the MySite is the one that is repeating.
I've modeled the class for this XML response as:
public class MYAPI
{
public List<MySite> MySite { get; set; }
public SomeOtherSite SomeOtherSite { get; set; }
}
public class MySite
{
public string Name { get; set; }
public string URL { get; set; }
public string SecondName { get; set; }
}
public class SomeOtherSite
{
public string Name { get; set; }
public string URL { get; set; }
}
And this is my code:
static void Main()
{
var handler = new HttpClientHandler();
handler.Credentials = new NetworkCredential("MyUsername", "MyPassword");
var client = new HttpClient(handler);
client.BaseAddress = new Uri("https://sitename.com:PortNumber/");
var formatters = new List<MediaTypeFormatter>()
{
new XmlMediaTypeFormatter(){ UseXmlSerializer = true }
};
var myApi = new MYAPI();
HttpResponseMessage response = client.GetAsync("/api/mysites").Result;
if (response.IsSuccessStatusCode)
{
myApi = response.Content.ReadAsAsync<MYAPI>(formatters).Result;
}
}
Now the myApi only has object for SomeOtherSite but the list for the MySite is empty.
Would someone please tell me how I should deserialize this response correctly?
Should I be creating custom media formatter? I have no idea of it by the way.
Also would you please tell me how to model that Resource attribute coming in the response?
And I can't change anything in the WebAPI server. I just need to consume the data from it and use it elsewhere.
Thank You so much!
I solved this after some really good direction from: https://stackoverflow.com/users/1124565/amura-cxg Much Thanks!
The solution was to annotate all the properties with XMLAttributes. And it correctly deserialized the response. And as for the Resource attribute, all I needed was [XmlAttribute(AttributeName="Resource")]
The rest of the source code works as is.
[XmlRoot(ElementName="MYAPI")]
public class MYAPI
{
[XmlElement(ElementName="MySite")]
public List<MySite> MySite { get; set; }
[XmlElement(ElementName="SomeOtherSite")]
public SomeOtherSite SomeOtherSite { get; set; }
}
public class MySite
{
[XmlElement(ElementName="Name")]
public string Name { get; set; }
[XmlElement(ElementName="URL")]
public string URL { get; set; }
[XmlElement(ElementName="SecondName")]
public string SecondName { get; set; }
[XmlAttribute(AttributeName="Resource")]
public string Resource { get; set; }
}
Plus, I didn't need any custom media formatter. And from one of the posts by https://stackoverflow.com/users/1855967/elisabeth , I learned that we should not touch the generated file from xsd.exe tool. So I explicitly set to use the XmlSerializer instead of the DataContractSerializer used by default:
var formatters = new List<MediaTypeFormatter>()
{
new XmlMediaTypeFormatter(){ UseXmlSerializer = true }
};
Since am new to web api, i am finding some difficulty to post json List to Web API.
Json
[
{
"ItemId":20,
"RegId":"VISIT0001778",
"BLoadDetailId":"8/31/2018 12:28:10 PM",
"OrderReferenceNo":null,
"StartTime":"0001-01-01T00:00:00",
"InvalidItemMsg":"",
"InvalidItemstatus":false,
"BLoadingBay":"Chute 009",
"BLoadingBayCode":null,
"BLoadingBayID":7,
"RFID":7123,
"GangId":2,
"BOrderTransfer":false,
"BLoadedBags":0.0,
"BRemainingBags":0.0,
"BConversionValue":null,
"WHid":2
}
]
class :
public class clsStartTimeUpdate
{
public int ItemId { get; set; }
public string RegId { get; set; }
public string BLoadDetailId { get; set; }
public string OrderReferenceNo{ get; set; }
public DateTime StartTime { get; set; }
public string InvalidItemMsg { get; set; }
public bool InvalidItemstatus { get; set; }
public string BLoadingBay { get; set; }
public string BLoadingBayCode { get; set; }
public int? BLoadingBayID { get; set; }
public long? RFID { get; set; }
public int? GangId { get; set; }
public bool BOrderTransfer { get; set; }
public decimal BLoadedBags { get; set; }
public decimal BRemainingBags { get; set; }
public string BConversionValue { get; set; }
public int? WHid { get; set; }
}
Json request
http://localhost:49290/api/config/Post?StartTimeDetails=[enter image description here][1][{%22ItemId%22:20,%22RegId%22:%22VISIT0001778%22,%22BLoadDetailId%22:%228/31/2018%2012:28:10%20PM%22,%22OrderReferenceNo%22:null,%22StartTime%22:%222001-01-01T00:00:00%22,%22InvalidItemMsg%22:%22%22,%22InvalidItemstatus%22:false,%22BLoadingBay%22:%22Chute%20009%22,%22BLoadingBayCode%22:null,%22BLoadingBayID%22:7,%22RFID%22:7123,%22GangId%22:2,%22BOrderTransfer%22:false,%22BLoadedBags%22:0.0,%22BRemainingBags%22:0.0,%22BConversionValue%22:null,%22WHid%22:2}]
Method WebAPI
[HttpPost]
public HttpResponseMessage Post([FromUri]List<clsStartTimeUpdate> StartTimeDetails)
{
return base.BuildSuccessResult(HttpStatusCode.OK, StartTimeDetails);
}
result:
[{"ItemId":0,"RegId":null,"BLoadDetailId":null,"OrderReferenceNo":null,"StartTime":"0001-01-01T00:00:00","InvalidItemMsg":null,"InvalidItemstatus":false,"BLoadingBay":null,"BLoadingBayCode":null,"BLoadingBayID":null,"RFID":null,"GangId":null,"BOrderTransfer":false,"BLoadedBags":0.0,"BRemainingBags":0.0,"BConversionValue":null,"WHid":null}]
return result doesnot assign the values as in the Json.
May be this is a simple situation , but i really appreciate the help.
It seems that you want to convey your json with HttpGet request instead of HttpPost then you can follow below,
1) Send Json with HttpGet
Method: Get
Url: http://localhost:49290/api/config/MyGet?StartTimeDetails=[{%22ItemId%22:20,%22RegId%22:%22VISIT0001778%22,%22BLoadDetailId%22:%228/31/2018%2012:28:10%20PM%22,%22OrderReferenceNo%22:null,%22StartTime%22:%220001-01-01T00:00:00%22,%22InvalidItemMsg%22:%22%22,%22InvalidItemstatus%22:false,%22BLoadingBay%22:%22Chute%20009%22,%22BLoadingBayCode%22:null,%22BLoadingBayID%22:7,%22RFID%22:7123,%22GangId%22:2,%22BOrderTransfer%22:false,%22BLoadedBags%22:0.0,%22BRemainingBags%22:0.0,%22BConversionValue%22:null,%22WHid%22:2}]
Web Api Method:
[HttpGet]
public HttpResponseMessage MyGet(string StartTimeDetails)
{
List<clsStartTimeUpdate> clsStartTimeUpdates = JsonConvert.DeserializeObject<List<clsStartTimeUpdate>>(StartTimeDetails);
return base.BuildSuccessResult(HttpStatusCode.OK, StartTimeDetails);
}
Note: Its bad practice to send huge json in query string, so for use HttpPost instead
2) Send Json with HttpPost
Method: Post
Url: http://localhost:49290/api/config/MyPost
Data:
[
{
"ItemId":20,
"RegId":"VISIT0001778",
"BLoadDetailId":"8/31/2018 12:28:10 PM",
"OrderReferenceNo":null,
"StartTime":"0001-01-01T00:00:00",
"InvalidItemMsg":"",
"InvalidItemstatus":false,
"BLoadingBay":"Chute 009",
"BLoadingBayCode":null,
"BLoadingBayID":7,
"RFID":7123,
"GangId":2,
"BOrderTransfer":false,
"BLoadedBags":0.0,
"BRemainingBags":0.0,
"BConversionValue":null,
"WHid":2
}
]
Web Api Method:
[HttpPost]
public HttpResponseMessage MyPost([FromBody]List<clsStartTimeUpdate> StartTimeDetails)
{
return base.BuildSuccessResult(HttpStatusCode.OK, StartTimeDetails);
}
For complex types Always use [FromBody] in the argument.
[HttpPost]
public HttpResponseMessage Post([FromBody]List<clsStartTimeUpdate> StartTimeDetails)
{
return base.BuildSuccessResult(HttpStatusCode.OK, StartTimeDetails);
}
And then specify your query object in Body.
Note: To specify the value in the body, You will need an API client like Postman or Swagger.
https://www.getpostman.com/
In Postman,
Select Post method and specify the URL,
Then go to "Body" tab and select raw.
Specify JSON as type.
In the body, paste your data.
{ [
{
"ItemId":20,
..........
}
]}
The Other answer by #ershoaib is the real fix for the problem that OP is facing. However, I am leaving this answer as it is the standard which should be followed.
Since you are using post you should expect the data in the controller method to come from body. See related issue here
I have a wordpress.org locally hosted on my pc.
I've installed a wordpress plugin called json-api which let you retrieve posts from your wordpress site.
I'm running the following code:
var client = new RestClient(BlogArticlesUrl);
var request = new RestRequest();
request.Timeout = 5000;
request.RequestFormat = DataFormat.Json;
request.Method = Method.GET;
request.AddParameter("json", "get_tag_posts");
request.AddParameter("slug", "featured");
request.AddParameter("count", "3");
var articles = client.Execute<List<BlogArticleModel>>(request);
After executing the code, in the variable articles I have the following:
Inside the Content there are few keys but I would only like to convert 'posts' to a model in c#
How do I acheive that?
EDIT:
I have found a solution using newtonsoft for dot net
Newtonsoft.Json.JsonConvert.DeserializeObject<BlogArticleResponse>(articles.Content);
In RestSharp, the Content is what gets deserialized. So, the type you pass into the .Execute<T> method must be the same structure as the response.
In your case, it will look something like this:
public class BlogArticleResponse
{
public string status { get; set; }
public int count { get; set; }
public int pages { get; set; }
public BlogTag tag { get; set; }
...
}
public class BlogTag
{
public int id { get; set; }
public string slug { get; set; }
public string title { get; set; }
public string description { get; set; }
...
}
You can then execute the request like this:
var result = client.Execute<BlogArticleResponse>(request);
For more information, have a look at the documentation.