The JSON value could not be converted to System.Nullable[System.Int32] - c#

I updated an ASP.NET Core 2.2 API to ASP.NET Core 3.0 and I am using System.Json:
services
.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0)
.AddJsonOptions(x => {})
I then tried to post JSON data using Angular 8, which was working before:
{
"name": "John"
"userId": "1"
}
The model in the ASP.NET Core 3.0 API is:
public class UserModel {
public String Name { get; set; }
public Int32? UserId { get; set; }
}
And the API Controller action is as follows:
[HttpPost("users")]
public async Task<IActionResult> Create([FromBody]PostModel) {
}
When I submit the model I get the following error:
The JSON value could not be converted to System.Nullable[System.Int32].
Do I need to do something else when using System.Json instead of Newtonsoft?

Microsoft has removed Json.NET dependency from ASP.NET Core 3.0 onwards and using System.Text.Json namespace now for serialization, deserialization and more.
You can still configure your application to use Newtonsoft.Json. For this -
Install Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet package
In ConfigureServices() add a call to AddNewtonsoftJson()-
services.AddControllers().AddNewtonsoftJson();
Read more on https://devblogs.microsoft.com/dotnet/try-the-new-system-text-json-apis/
https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to

Here Via the json you pass a string value for UserId but your model refer a int32? value for UserId. Then how your value convert from string to int32?

I faced this issue! and all those solutions did not work for me! so I did the following:-
First of all the data returned to me as the following:-
I need to make year as an int and also want to make value as double!
You should make custom JsonConverter, it worked for me after a lot of search, and here is sample of:-
StringToDoubleConverter
public sealed class StringToDoubleConverter : JsonConverter<double>
{
public override double Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
double.TryParse(reader.GetString(),out double value);
return value;
}
public override void Write(
Utf8JsonWriter writer,
double value,
JsonSerializerOptions options)
{
throw new NotImplementedException();
}
}
Then you can save it to db! play around as you like

In my scenario I was just sending the "", not the null :)

Related

Custom System.Text JsonConverter<DateTimeOffset> not getting invoked

I am creating a custom JsonConverterto parse datetimeoffset, to fix utc issue with offset. I am following MS doc
using System.Globalization;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace SystemTextJsonSamples
{
public class DateTimeOffsetJsonConverter : JsonConverter<DateTimeOffset>
{
public override DateTimeOffset Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options) =>
DateTimeOffset.ParseExact(reader.GetString()!,
"MM/dd/yyyy", CultureInfo.InvariantCulture);
public override void Write(
Utf8JsonWriter writer,
DateTimeOffset dateTimeValue,
JsonSerializerOptions options) =>
writer.WriteStringValue(dateTimeValue.ToString(
"MM/dd/yyyy", CultureInfo.InvariantCulture));
}
}
I have registered the converter in the startup like so
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase));
options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
options.JsonSerializerOptions.Converters.Add(new DateTimeConverter());
options.JsonSerializerOptions.Converters.Add(new DateTimeOffsetConverter());
})
and here is my model
[Serializable()]
public class Travel
{
public DateTimeOffset TravelTime { get; set; }
}
When i make call to my api, my custom converter for datetimeoffset is not getting called. Please note that i also have a customdate converter which is working as expected.
Why is my offsetdatetime converter not getting invoked when i serialize/deserialize.
I am using .Net core 6
It's not enough to define a JsonConverter, you also have to apply it to the property, like this:
[JsonConverter(typeof(DateTimeOffsetJsonConverter))]
public DateTimeOffset TravelTime { get; set; }
Then it will be used for serialization and deserialization.
The reason for this (like why can't it pick up the type?) is that you can have several converters defined for the same type and apply them to the properties that need them. Now you can actually make different 'string to string' converters (for example).
Firstly, it should be DateTimeOffsetJsonConverter instead of DateTimeOffsetConverter, change your code to:
builder.Services.AddControllersWithViews().AddJsonOptions(options =>
{
options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase));
options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
options.JsonSerializerOptions.Converters.Add(new DateTimeConverter());
//options.JsonSerializerOptions.Converters.Add(new DateTimeOffsetConverter());
options.JsonSerializerOptions.Converters.Add(new DateTimeOffsetJsonConverter());
});
Then, be sure you post the data with content type application/json. For example:
Besides, you can also add [FromBody] to specify the source because it binds the form data by default if you use asp.net core MVC project:
[HttpPost]
public IActionResult Index([FromBody]Travel model)
{
//do your stuff...
}

Trimming all posted strings to an API ASP.NET CORE 3.x

Information for this as to the many .Net versions is all over the place and I cannot find a concrete up to date example.
I am attempting to automatically trim all the “string” values I post to my API’s automatically.
Note this is ASP.NET CORE 3.x which has introduced the new namespaces “System.Text.Json” etc. Rather than the Newtonsoft ones most old examples are using.
The Core 3.x API’s do not use Model Binding but rather JsonConverter(s) that I am attempting to override, therefore model binding examples are not relevant here.
The following code does work but it means that I must put the annotation:
[JsonConverter(typeof(TrimStringConverter))]
Above each string in the API models I am posting too.
How can I do this so that it just does it to anything defined as a string in all the API models globally?
// TrimStringConverter.cs
// Used https://github.com/dotnet/runtime/blob/81bf79fd9aa75305e55abe2f7e9ef3f60624a3a1/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/JsonValueConverterString.cs
// as a template From the DotNet source.
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace User
{
public class TrimStringConverter : JsonConverter<string?>
{
public override string? Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
return reader.GetString().Trim();
}
public override void Write(
Utf8JsonWriter writer,
string? value,
JsonSerializerOptions options)
{
writer.WriteStringValue(value);
}
}
}
// CreateUserApiModel.cs
using System.Text.Json.Serialization;
namespace User
{
public class CreateUserApiModel
{
// This one will get trimmed with annotation.
[JsonConverter(typeof(TrimStringConverter))]
public string FirstName { get; set; }
// This one will not.
public string LastName { get; set; }
}
}
// ApiController
[HttpPost]
[Route("api/v1/user/create")]
public async Task<IActionResult> CreateUserAsync(CreateUserApiModel createUserApiModel)
{
// createUserApiModel.FirstName << Will be trimmed.
// createUserApiModel.LastName, << Wont be trimmed.
return Ok("{}");
}
As to the comment by #pinkfloydx33 above the following seems to work correctly.
// Startup.cs
services.AddControllers()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.Converters.Add(new TrimStringConverter());
});
Also for note, anybody using the code in the original question. The Converter only trims on Read and not on write unless you add another trim(). I only required it one way.

Pass JSON object with a TimeSpan property to C# WebAPI

I have a WebAPI (written in C#), a POST-method accepting a complex object with a System.TimeSpan-Property named TriggerDelay, and a React Native application from where I am passing this object in JSON format.
However, this TimeSpan-property is not serializing properly and I keep getting 00:00:00-value on the API side.
I am trying like this:
"triggerDelay":{
"hours": "30",
"minutes": "10",
"seconds": "0"
},
OR like this:
"triggerDelay": "30:10:00"
But still no luck... In the API, it is always 00:00:00.
I would appreciate any help!
UPD Here is my Model:
public class Alarm
{
public Guid Id { get; set; } = Guid.NewGuid();
[...other properties...]
public TimeSpan TriggerDelay {get; set;}
}
My WebAPI Method:
public async Task<IActionResult> Publish([FromBody] Alarm alarm) {}
And here is my raw JSON object, set in the body of the request in Postman:
{
"id": "d17ef748-f378-4728-c6c2-9dfab1efce5b",
[...other properties...]
"triggerDelay":{
"hours": "30",
"minutes": "10",
"seconds": "0"
}
}
Newtonsoft's Json.NET supports TimeSpan serialization/deserializion out of the box (how to switch to Newtonsoft.Json in an ASP.NET Core 3.0 MVC project if you decide to) :
public class MyClass
{
public TimeSpan Interval { get; set; }
}
var json = #"{ ""Interval"":""00:00:42""}";
Console.WriteLine(JsonConvert.DeserializeObject<MyClass>(json).Interval.TotalSeconds); // prints 42
System.Text.Json (the default json handling tool in ASP.NET Core since 3.0 which, it seems, you are using) does not have built-in support for TimeSpan at the moment, so you will need to implement custom converter. Simplest one would look like this:
public class TimeSpanConverter : System.Text.Json.Serialization.JsonConverter<TimeSpan>
{
public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return TimeSpan.Parse(reader.GetString());
}
public override void Write(Utf8JsonWriter writer, TimeSpan value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString());
}
}
And usage:
public class MyClass
{
[System.Text.Json.Serialization.JsonConverterAttribute(typeof(TimeSpanConverter))]
public TimeSpan Interval { get; set; }
}
Console.WriteLine(System.Text.Json.JsonSerializer.Deserialize<MyClass>(json).Interval.TotalSeconds); // prints 42

Date Only Attribute in .NET Core 3.1

I am migrating a C# API from .NET Framework to .NET Core 3.1.
I have a requirement that some fields return yyyyMMdd only (no time)
and other fields that would return the full DateTime Value (Date and Time).
In the old .NET Framework world, we could make a quick converter like this:
public class OnlyDateConverter : IsoDateTimeConverter
{
public OnlyDateConverter()
{
DateTimeFormat = "yyyyMMdd";
}
}
and use it in my model like
[JsonConverter(typeof(DateTimeConverter))]
public DateTime OrderDate { get; set; }
That isn't working in .NET Core 3.1.
When I call it via Swagger, my JSON that is returned is:
"OrderDate": "2002-05-22T00:00:00"
I know you can add a JsonSerializerOption in Startup.cs, however that will force all dates to use the same formatting. I need to pick and choose.
I have tried:
making multiple json converters, however they never get called/work
[DataType(DataType.Date)]
[JsonConverter(typeof(DateTimeConverter))]
I have spent all day on this. I'm hoping someone has done this and can point out my silly mistake.
This code work for me
in your output model add this :
[DataType(DataType.Date)]
[JsonConverter(typeof(JsonDateConverterExtension))]
public DateTime? DateOfBirth { get; set; }
where JsonDateConverterExtension is :
public class JsonDateConverterExtension : JsonConverter<DateTime?>
{
public override DateTime? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> DateTime.ParseExact(reader.GetString(),
"yyyy-MM-dd", CultureInfo.InvariantCulture);
public override void Write(Utf8JsonWriter writer, DateTime? value, JsonSerializerOptions options)
=> writer.WriteStringValue(value?.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture));
}

.Net core 3.0 API doesn't bind properties with hyphen

Reviewed the question as it was not received well the last time. Hope I have provided all the required information below.
I have a basic API controller and my Json object doesn't seem to bind to the model properly. The root object binds but the property with hyphen in its name doesn't bind. Unfortunately, I cannot drop the hyphen in the property name.
How do I get the property to bind correctly?
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
namespace TestCoreAPI.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class TestController : ControllerBase
{
// POST: api/Test
[HttpPost]
public string Post([FromBody] TestPayload testPayload)
{
if (testPayload == null)
{
return "Test payload is empty";
}
if (string.IsNullOrWhiteSpace(testPayload.TestProperty))
{
return "Test property is empty";
}
return "Valid input - " + testPayload.TestProperty;
}
}
[JsonObject("test-payload")]
public class TestPayload
{
[JsonProperty(PropertyName = "test-property")]
public string TestProperty { get; set; }
}
}
This is the call I'm making to the API
POST /api/test HTTP/1.1
Content-Type: application/json
{"test-property":"some string value"}
Net Core 3.1 does bind hyphens. Either way, the two options are Newtonsoft.Json or new-in-core-3 System.Text.Json, and they use slightly different Attribute names:
public class PostModel
{
//This one if you are using Newtonsoft.Json
//The Nuget dependency is Microsoft.AspNetCore.Mvc.NewtonsoftJson
[JsonProperty(PropertyName = "kebab-case-json-field")]
//This one of you are using the new System.Text.Json.Serialization
[JsonPropertyName("kebab-case-json-field")]
public string kebabCaseProperty { get; set; }
}
Meanwhile, in your Startup.cs, to use Newtonsoft, you need AddMvc(), whereas for new System.Text.Json, you don't. These both worked for me:
public void ConfigureServices(IServiceCollection services)
{
//if using NewtonSoft
services.AddMvc().AddNewtonsoftJson();
//if using System.Text.Json.
//This is the code that core 3 `dotnet new webapi` generates
services.AddControllers();
}
Are you adding AddNewtonsoftJson() to your services in Startup.ConfigureServices ? If not, the new System.Text.Json is being used, not Newtonsoft. I think you'll need to do AddNewtonSoftJson() as i'm fairly sure System.Text.Json doesn't support 'kebab case' bindings.
https://learn.microsoft.com/en-us/aspnet/core/migration/22-to-30?view=aspnetcore-3.1&tabs=visual-studio#newtonsoftjson-jsonnet-support

Categories