Dotnet core MVC xml parameter binding always give null - c#

I've read a few questions on SO but the solutions are all for ASP.NET webApi not dotnet core.
I've added xml support in my Startup.cs
services
.AddMvc()
.AddXmlSerializerFormatters();
Here's my controller method:
[Route("main")]
[HttpPost]
public string MainPost([FromBody]MessageModel msg)
{
_log.LogInformation("body msg ="+msg.Content);
return "test";
}
Here's my XML
<xml>
<ToUserName>toUser</ToUserName>
<FromUserName>FromUser</FromUserName>
<CreateTime>1348831860</CreateTime>
<MsgType>text</MsgType>
<Content>test content</Content>
<MsgId>1234567890123456</MsgId>
</xml>
Here's my Model class:
[XmlRoot("xml")]
public class MessageModel
{
public string ToUserName { get; set; }
public string FromUserName { get; set; }
public DateTime CreateTime { get; set; }
public string MessageType { get; set; }
public string Content { get; set; }
public int MsgId { get; set; }
}
When I send post request(with header "application/xml") to this route, it always gives me null for MessageModel msg
What am I missing here?
I know there's a DataContract thing but I can't control the xml format sending
to my server so I need a way to bind xml in the format stated above to my object.
Any help or lead to any document will be much appreciated.

I've found the problem thanks to #jdweng 's comment which gives me a idea on what might go wrong.
The problem is that there are some elements in the XML cannot be serialized to the designated type in my model class. They're DateTime CreateTime and int MsgId.
So changing all the fields to string solved problem.
I don't know whether why isn't dotnet core tells us about this instead of just giving us null if the binding fails due to this kind of reason, this is definitely something they can improve on.

Related

C# getting MongoDB BsonDocument to return as JSON

I asked a question a couple of days ago to collect data from MongoDB as a tree.
MongoDB create an array within an array
I am a newbie to MongoDB, but have used JSON quite substantially. I thought using a MongoDB to store my JSON would be a great benefit, but I am just experiencing immense frustration.
I am using .NET 4.5.2
I have tried a number of ways to return the output from my aggregate query to my page.
public JsonResult GetFolders()
{
IMongoCollection<BsonDocument> collection = database.GetCollection<BsonDocument>("DataStore");
PipelineDefinition<BsonDocument, BsonDocument> treeDocs = new BsonDocument[]
{
// my query, which is a series of new BsonDocument
}
var documentGroup = collection.Aggregate(treeDocs).ToList();
// Here, I have tried to add it to a JsonResult Data,
// as both documentGroup alone and documentGroup.ToJson()
// Also, loop through and add it to a List and return as a JsonResult
// Also, attempted to serialise, and even change the JsonWriterSettings.
}
When I look in the Immediate Window at documentGroup, it looks exactly like Json, but when I send to browser, it is an escaped string, with \" surrounding all my keys and values.
I have attempted to create a model...
public class FolderTree
{
public string id { get; set; }
public string text { get; set; }
public List<FolderTree> children { get; set; }
}
then loop through the documentGroup
foreach(var docItem in documentGroup)
{
myDocs.Add(BsonSerializer.Deserialize<FolderTree>(docItem));
}
but Bson complains that it cannot convert int to string. (I have to have text and id as a string, as some of the items are strings)
How do I get my MongoDB data output as Json, and delivered to my browser as Json?
Thanks for your assistance.
========= EDIT ===========
I have attempted to follow this answer as suggested by Yong Shun below, https://stackoverflow.com/a/43220477/4541217 but this failed.
I had issues, that the "id" was not all the way through the tree, so I changed the folder tree to be...
public class FolderTree
{
//[BsonSerializer(typeof(FolderTreeObjectTypeSerializer))]
//public string id { get; set; }
[BsonSerializer(typeof(FolderTreeObjectTypeSerializer))]
public string text { get; set; }
public List<FolderTreeChildren> children { get; set; }
}
public class FolderTreeChildren
{
[BsonSerializer(typeof(FolderTreeObjectTypeSerializer))]
public string text { get; set; }
public List<FolderTreeChildren> children { get; set; }
}
Now, when I look at documentGroup, I see...
[0]: {Plugins.Models.FolderTree}
[1]: {Plugins.Models.FolderTree}
To be fair to sbc in the comments, I have made so many changes to get this to work, that I can't remember the code I had that generated it.
Because I could not send direct, my json result was handled as...
JsonResult json = new JsonResult();
json.Data = documentGroup;
//json.Data = JsonConvert.SerializeObject(documentGroup);
json.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
return json;
Note, that I also tried to send it as...
json.Data = documentGroup.ToJson();
json.Data = documentGroup.ToList();
json.Data = documentGroup.ToString();
all with varying failures.
If I leave as documentGroup, I get {Current: null, WasFirstBatchEmpty: false, PostBatchResumeToken: null}
If I do .ToJson(), I get "{ \"_t\" : \"AsyncCursor`1\" }"
If I do .ToList(), I get what looks like Json in json.Data, but get an error of Unable to cast object of type 'MongoDB.Bson.BsonInt32' to type 'MongoDB.Bson.BsonBoolean'.
If I do .ToString(), I get "MongoDB.Driver.Core.Operations.AsyncCursor`1[MongoDB.Bson.BsonDocument]"
=========== EDIT 2 =================
As this way of extracting the data from MongoDB doesn't want to work, how else can I make it work?
I am using C# MVC4. (.NET 4.5.2)
I need to deliver json to the browser, hence why I am using a JsonResult return type.
I need to use an aggregate to collect from MongoDB in the format I need it.
My Newtonsoft.Json version is 11.0.2
My MongoDB.Driver is version 2.11.1
My method is the simplest it can be.
What am I missing?

Using a REST Api, how to include "any kind of json" in my typed request model?

I am using .NET Framework and ASP.NET Core to create a REST web Api.
This web api has a call that gets a request model to save data and some call that later retrieves the data.
Most of the data is structured information I need in the backend and it is saved into different fields and tables in the database. On retrieval it is loaded from those tables and returned.
This all works.
However, I now have a requirement where the caller wants to save and later retrieve arbitrary data (lets just say a random json) as one of those fields. I can save and load json from the database that is not a problem, my problem is to build the web api model for my request.
[HttpPost]
public IActionResult Save([FromBody] ApiCallRequestModel request)
{
// ...
}
public sealed class ApiCallRequestModel
{
// structured, well known information
public int? MaybeSomeNumber { get; set; }
[Required]
public string SomeText { get; set; }
[Required]
public SubModel SomeData { get; set; }
// one field of unknown json data
public ??? CustomData { get; set; }
}
I could think of dynamic or maybe even ExpandoObject or JObject to try and I might, but I would like a solution that works because it's best practice, not just because I tried and it didn't fail today with my simple tests.
If everything else fails, I could just make the field a string and tell the client to put serialized json into it. But that's a workaround I would see as a last resort if this question yields no answers.
It has proven to be extremly hard to google this topic, since all words I would use lead me to pages explaining Json serialization of my request model itself. I know how that works and it's not a problem. The mix of structured data and free json is what I cannot find out from a somewhat authorative source.
So what type would you use here, what is the best practice for receiving arbitrary json in one property of your model?
So to sum this up, as suggested I used a JToken from the Json.NET nuget package, since I already had that package in my project.
[HttpPost]
public IActionResult Save([FromBody] ApiCallRequestModel request)
{
// ...
}
public sealed class ApiCallRequestModel
{
// structured, well known information
public int? MaybeSomeNumber { get; set; }
[Required]
public string SomeText { get; set; }
[Required]
public SubModel SomeData { get; set; }
// one field of unknown json data
public JToken CustomData { get; set; }
}
Works like a charm.

Asp.Net WebApi model binding nullable datetime

Hello,
I have api GET-method /rating (ASP.Net WebApi 2.1), which accepts objects of type ChartPageRequest:
// comments're removed for readability
public sealed class ChartPageRequest
{
public DateTime? From { get; set; }
public DateTime? To { get; set; }
public string Cursor { get; set; }
[Range(-100, 100)]
public int Take { get; set; } = 10;
}
/rating method has following signature:
[HttpGet]
[Route("rating")]
[ResponseType(typeof(ChartPage))]
[ValidateModelState]
public async Task<IHttpActionResult> GetTranslationRatingChartAsync([ModelBinder] ChartPageRequest model)
{
// body here
}
And ValidateModelState attribute is just a custom attribute which returns custom response when ModelState isn't valid. It doesn't check anything by itself except HttpActionContext.ModelState.IsValid property.
This api method works fine except one case - when client explicitly passes null value to DateTime? properties of ChartPageRequest, e.g.:
/rating?from=2016-07-08 12:01:55.604&to=null
In this case ValidateModelState attribute registers invalid ModelState with following message: The value 'null' is not valid for To.
I've found this kind of problem quite popular, but haven't found any good workarounds without creating custom model binder. So here's the questions:
Is there another approach without custom model binder?
In case there isn't, how can I take only "accepting null" job and leave DateTime parsing to default binder in my custom binder?
Do I need to accept those nulls at all? All clients are developing by my colleagues so I can force them to not send it. Is it good practice after all?
Thanks!
I didn't validate this method, but I guess you can try parse it yourself, though I think it's not a convenient way, first use a string to get the To:
public string To { get; set; }
And give another property DateTimeTo and parse To yourself:
public DateTime? DateTimeTo { get; set; }
public void ParseTo()
{
if(To.ToLower() == "null")
DateTimeTo = null;
else
DateTimeTo = Convert.ToDateTime(To);
}
And DateTimeTo is the parsed result property.
I recently ran into some Asp.Net WebApi parsing body issue, the parsing doesn't work very well in some condition.

receiving a json string in C# Web Api 2

I'm trying to do a patch with web api. I keep getting NULL for my json. Please Help
Here is my Json
[{"PartNumber":"AN33016UA-VB"}{"Category":"Chassis"}]
Here is my my class
public class wsCategory
{
public string PartNumber { get; set; }
public string Category { get; set; }
}
Here is my Api Controller
[HttpPatch]
[ActionName("IMDSCategory")]
public HttpResponseMessage IMDSCategory([FromBody]wsCategory jsonbody)
{
var data = jsonbody.PartNumber;
return new HttpResponseMessage(HttpStatusCode.Created);
}
The JSON is inavalid.
[{"PartNumber":"blahblah","Category":"Chassis"}]
I believe the array container will be parsed out correctly, but I'm on a chromebook right now, so I can't check that. If it still fails, drop the [].
based on your method
[HttpPatch]
[ActionName("IMDSCategory")]
public HttpResponseMessage IMDSCategory([FromBody]wsCategory jsonbody){...}
Your JSON is invalid given the model you are trying to parse.
[{"PartNumber":"AN33016UA-VB"}{"Category":"Chassis"}]
should be
{"PartNumber":"AN33016UA-VB","Category":"Chassis"}

WebAPI - Array of Objects not deserializing correctly on server side

In the client-side, I am using AngularJS and in the server-side I am using ASP.NET WebAPI.
I have two view models, ProductCriteriaViewModel and SimpleDisplayFieldViewModel:
public class ProductCriteriaViewModel
{
public int ID { get; set; }
public int? UserSearchID { get; set; }
public bool? Enabled { get; set; }
public SimpleDisplayFieldViewModel Property { get; set; }
public string Operator { get; set; }
public string CriteriaValue { get; set; }
}
public class SimpleDisplayFieldViewModel
{
public string Name { get; set; }
public string Value { get; set; }
public string PropertyType { get; set; }
}
In Angular, I submit a POST request to a WebAPI controller action with the following signature:
public IList<...> FindProducts(List<ProductCriteriaViewModel> criteriaVM, bool userFiltering)
{
...
}
In testing, I tried to send an array of Product Criterias, and checked Fiddler to see what the array looked like in the body of the POST request when it was being sent to the server. This is what the array looked like:
[
{"Enabled":true,
"Operator":"Less than",
"Property":
{"$id":"2",
"Name":"Copyright Year",
"Value":"Basic",
"PropertyType":null},
"CriteriaValue":"2013",
"IsNew":true},
{"Enabled":true,
"Operator":"Greater Than",
"Property":
{"$id":"2",
"Name":"Copyright Year",
"Value":"Basic",
"PropertyType":null},
"CriteriaValue":"1988",
"IsNew":true}
]
The above array has the correct values, however the result of deserialization on the server-side is incorrect. This is where it gets strange.
After the server deserializes the array and arrives in the controller action, the first element in criteriaVM is correct, all the values are set properly. However the second element is incorrect, CriteriaValue and Property are nulled out:
This issue only occurs whenever I choose the same search property for more than one criteria (i.e. Copyright < 2013 and Copyright > 1988). However, if I choose different properties (i.e. Copyright < 2013 and Price > 20), then all elements in the resulting criteriaVM are correctly initialized.
I do not understand what could be causing this issue. Why are only CriteriaValue and Property set to null in the second element of the List? Why does this issue only occur when I choose multiples of the same search properties?
Json.NET uses the keywords $id and $ref in order to preserve object references, so you are having troubles with your deserialization because your JSON has "$id" in the "Property" object. See this link for more information about object references.
In order to fix your deserialization issues, you can add the following line in the Register method of your WebApiConfig.cs class
config.Formatters.JsonFormatter.SerializerSettings.MetadataPropertyHandling = MetadataPropertyHandling.Ignore;
If your Web Api project does not include a WebApiConfig.cs class, simply add the configuration in your Global.asax:
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.MetadataPropertyHandling = MetadataPropertyHandling.Ignore;
Now your object in the web api method should look like this:

Categories