ASP.NET Web API: DateTime binding not working in json body - c#

For DateTime type, if I have a DateTime property inside a class then I try to model bind that class with HTTP post json body, the DateTime property does not get bound, how so? But if I use parameter binding, it's working fine. So below code works
[Route("v1/taxiqi")]
[HttpPost]
public object UpdateTaxiQI(string status, DateTime updateTime)
{
...
}
But not for below code
[Route("v1/taxiqi")]
[HttpPost]
public object UpdateTaxiQI(TaxiQI taxiQI)
{
...
}
public class TaxiQI
{
public string Status;
public DateTime UpdateTime;
}
I am using the latest ASP.NET Web API. Fields are working as it is working on my other API, besides, Status field is successfully bound. And I already tried properties, same result.
Sorry, I found out the root cause, it is caused by our code base is using a custom DateTimeConverter for JSON.NET and it expects a JavaScript style ticks instead of a date string

Your TaxiQI class is incorrect.
The JSON works with public properties not public fields. Change your class to this:
public class TaxiQI
{
public string Status { get; set; }
public DateTime UpdateTime { get; set; }
}

In addition to Svek's answer, it may be wiser to use a Nullable<DateTime> or DateTime? instead. Null values are an intrinsic possibility whenever you deserialize data.

I found out the root cause, my bad, it is caused by our code base is using a custom DateTimeConverter for JSON.NET and it expects a JavaScript style ticks instead of a date string

Related

Type binding asp core 2.2

My asp core api gets as input object with the following json structure:
{
"id": 1,
"zoneDate": {
"zone": "Paris/France",
"dateTime": "2019-04-02T00:00:00"
}
}
I wanna convert this object to the following model:
public class Model
{
public int Id { get; set; }
public DateTime DateTime { get; set; }
}
I have a service that knows how to convert complex json object zoneDate to build in .Net DateTime with an offset.
I need api to do the conversion automatically (in model binding) whenever it sees DateTime in the model, which is marked with some custom attribute (or built in if any). Any ideas to do that elegantly? Is there something like TypeConverter that can work on single simple type property and that can take parameter injected through constructor? Any help is deeply appreciated!
EDIT:
I need the binding on the property level, not on the model level. I mean if I use model binder, then I need to create it for EACH and EVERY new model which have DateTime props that should be created from ZoneDate json. That's not what I want. I want to treat ZoneDate as simple type, when I convert it to DateTime which has an attribute or some name convention
UPDATE:
Finally, the #jpgrassi advice was implemented.
Instead of writing these binders, that just hide the complexity away..
why not introduce a type ZoneDate that contains those fields and let
the default modelbinder do its work? In the end, what will you gain
from accepting that format, and binding it yourself? I think having a
type is way better. You let the compiler help you with stuff
(refactoring and etc). Plus your code is simple.
First of all Paris/France is not valid Timezone text format, the correct should be Europe/Paris.
Then I assume you want to convert the given the time
using Newtonsoft.Json.Linq;
using System;
using System.Globalization;
using TimeZoneConverter;
namespace ParseJsonDateTimeZone
{
class Program
{
static void Main(string[] args)
{
string json = "{\"id\": 1,\"zoneDate\":{\"zone\":\"Europe/Paris\",\"dateTime\":\"2019-04-02T00:00:00\"}}";
Console.WriteLine(new Model(json));
}
public class Model
{
public Model(string json)
{
JObject jObject = JObject.Parse(json);
Id = (int) jObject["id"];
JToken zoneDate = jObject["zoneDate"];
TimeZoneInfo tzi;
try
{
tzi = TZConvert.GetTimeZoneInfo(zoneDate["zone"].ToString());
}
catch(TimeZoneNotFoundException)
{
// Fallback to UTC or do something else?
tzi = TZConvert.GetTimeZoneInfo("UTC");
}
CultureInfo culture = CultureInfo.InvariantCulture;
DateTime dateTime = TimeZoneInfo.ConvertTimeFromUtc(DateTime.Parse(zoneDate["dateTime"].ToString()), tzi);
DateTime = TimeZoneInfo.ConvertTime(dateTime, tzi );
}
public int Id { get; set; }
public DateTime DateTime { get; set; }
}
}
}

ASP.NET WebAPI OData Delta object fields is null in PATCH request

I have created ASP.NET WebAPI with OData support.
The Project class has a field named ProjectType with nullable integer type declaration:
public class Project
{
public string Id { get; set; }
public string Name { get; set; }
public int? ProjectType { get; set; }
}
My WebAPI Controller:
public class LocalModelsController : ODataController
{
public IHttpActionResult Patch([FromODataUri] string key, Delta<Project> patch)
{
var projectUpdated = patch.GetEntity();
var projectType = projectUpdated.ProjectType;
//projectType is null here
}
}
When I send the PATCH http request (with BSON content type format) from client to WebAPI, the Project object has correct values of Id and Name, but ProjectType is always null.
But when I change the type of ProjectType field type from int? to double?, it works correct:
public double? ProjectType { get; set; }
Is it a limitation of ODATA or I were missing something?
I have worked it out. It is just because I was using BSON format to transfer objects throught HTTP request. It works well if I use JSON instead. Not sure what wrong with BSON format though.
I know this is an old post but I wanted to drop my solution here for any future readers it may help.
I'm thinking this must be a bug because it makes no sense, but my issue stemmed from JSON.stringify() putting quotes around all my fields then when I posted the data, the odata endpoint couldnt convert to a decimal for one of my properties. I changed the property type from a decimal to an int and the patch started working.
Alternatively you can use postman to send a patch request, one with the quotes and one without. This is how I found my issue.
Im shocked that odata wouldnt be able to deserialize from a string to a decimal but thats exactly what it does.
You probably just need to cast projectUpdated as the correct type.
For example
var projectUpdated = patch.GetEntity();
Project p = projectUpdated as Project;
if (p != null)
{
var projectType = p.ProjectType;
}

ServiceStack Json deserializing incorrectely

I've got a RequestDto, let's say Class A Dto, it contains a self defined type property:
// C# code
public Class MyObject
{
public string A { get; set; }
public string B { get; set; }
}
public Class ADto
{
public List<MyObject> MO { get; set;}
}
When I am trying to send the Dto using Json, the Json object looks like this:
{"MO":[{"A":"String","B":"a"},{"A":"String","B":"b"}]}
but the object I am receiving will be null.
However if I change the Json string into:
{MO:[{A:"String",B:"a"},{A:"String",B:"b"}]}
I lose the quotation marks on the objects' names and it works.
The correct format of Json should include those quotation marks right?
Why is this happening?
ServiceStack does serializes and deserializes valid JSON which requires every property name to be quoted however you're saying that the text below works:
{MO:[{A:"String",B:"a"},{A:"String",B:"b"}]}
However this isn't valid JSON, it instead looks like ServiceStack's JSV Format
You haven't mentioned where you're sending the JSV Format or have provided the Raw HTTP Request (for us to work it out), but I'm assuming if you're using Postman (mentioned in your comments) than you're trying to send JSON in the ?QueryString which isn't allowed.
But ServiceStack does support sending complex object graphs on the QueryString using JSV.
Since you're sending a complex type you'd either POST the request as either JSON or www-form-urlencoded Form Data or if you want to pass it in the QueryString you need to convert it to JSV.
In future please include the Raw HTTP Request as this question lacks any context on where you're changing the JSON string, how you're trying to use it or what's actually being sent, where you're sending it to, etc - making it impossible to guess what the issue is.
Change your class to
public Class MyObject
{
public string Mobile { get; set; }
public string Name { get; set; }
}
public Class ADto
{
public List<MyObject> MO { get; set;}
}
Then your json should be
{MO:[{Mobile:"0556604",Name:"Peter"},{Mobile:"4565466",Name:"John"}]}

WebApi Controller model serialization issue

I'm facing little stranger issue with Web API controller. I have a collection which is being passed in an action of api controller. Object being used is collection is having 4 properties.
My action is able to accept collection parameter when it's properties are in specific order. See below :-
[HttpPost]
public ForexRates UpdateRates([FromBody] Rates rates)
{
// TODO: Obviously code :)
return rates;
}
This code is being place in API controller & calling from Postman. See below:-
<rates>
<rate>
<id>fefef</id>
<rate>35353.333</rate>
<series>dfefge</series>
<series-order>sfefefef</series-order>
</rate></rates>
If I change the order of the properties I started getting null value in my action. Can some one please explain this :)
Models
public class Rate
{
public string Id { get; set; }
public string Date { get; set; }
public double Rate { get; set; }
}
public class Rates : Collection<ForexRate>
{
}
You will need to control the order with which your XML is serialized. Use XmlElementAttribute and specify the Order.
There is a similar question here
FYI, I suppose there is no way for you to change the order of the properties, while you supply from PostMan to your WebApi service. You will need to follow the exact order.
If you don't wanna do that, then pass this Xml as a string parameter and then parse it inside a method.
The default binder can have issues when the same name is used in different places during binding.
In your case you've got Rate.Rate - both class name and property name. Try changing your class to (and corresponding xml for the post) :
public class Rate
{
public string Id { get; set; }
public string Date { get; set; }
public double Value { get; set; }
}
and then try changing the order.
While I don't have a definitive reason why it works in one order and not another, it's likely that when it gets to the Rate(double) value it tries to create a new Rate(object) but doesn't have the correct properties (as its just a double).
A more complicated solution would be to write a specific model binder for the Rate object.
The issue has to do with the DataContractSerializer which expects the elements to occur in a specific order (alphabetical with some consideration given to inheritance). That's the default serializer used when creating a Web API project.
You can override this and specify a different serializer during API Configuration like this:
GlobalConfiguration.Configuration.Formatters.XmlFormatter
.SetSerializer<SomeType>(new XmlSerializer(typeof(SomeType)));

Serializer ignores properties that use another serialized property

I am working on an endpoint in asp.net that serializes and returns some data, using the default serializer.
The consuming applications are transitioning between changing names for properties (in other words, some existing applications are using names like ...Vat; while newer ones are using ...Tax. I therefore need to keep both names in the response for the moment, until these changes are complete.
The return type is IList.
public class Product
{
...
public decimal PriceIncVat { get; set; }
public decimal PriceIncTax { get { return PriceIncVat; } }
public int TaxCode { get; set; }
...
}
However, when I examine the response in fiddler, only the PriceIncVat property exists in the json list of products.
I can't think of any reason why the above wouldn't work. I added TaxCode at the same time as PriceIncTax, and it is returned, so I know the code of the endpoint is up to date.
And on the client side of a newer client project we have:
public class ProductDto
{
...
public decimal PriceIncTax { get; set; }
public string TaxCode { get; set; }
...
}
Very confused here.
The serializer assumes you will need to deserialize the data some time. Hence by default only properties with a getter and a setter are considered.
When using the DataContractJsonSerializer, it's possible to turn on serialization of read-only properties using the SerializeReadOnlyTypes property (despite its rather misleading name).
Side note: Check-out the Json.NET serializer, which gives more options and better control over the (de)serialization process.

Categories

Resources