Empty String Not Being Converted to Null When Passing Json Object to Controller

string.empty converted to null when passing JSON object to MVC Controller

This is a MVC feature which binds empty strings to nulls.

This logic is controlled with the ModelMetadata.ConvertEmptyStringToNull property which is used by the DefaultModelBinder.

You can set the ConvertEmptyStringToNull with the DisplayFormat attribute

public class OrderDetailsModel
{
[DisplayFormat(ConvertEmptyStringToNull = false)]
public string Comment { get; set; }

//...
}

However if you don't want to annotate all the properties you can create a custom model binder where you set it to false:

public class EmptyStringModelBinder : DefaultModelBinder 
{
public override object BindModel(ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
bindingContext.ModelMetadata.ConvertEmptyStringToNull = false;
Binders = new ModelBinderDictionary() { DefaultBinder = this };
return base.BindModel(controllerContext, bindingContext);
}
}

And you can use the ModelBinderAttribute in your action:

public ActionResult SaveOrderDetails([ModelBinder(typeof(EmptyStringModelBinder))] 
OrderDetailsModel orderDetailsModel)
{
}

Or you can set it as the Default ModelBinder globally in your Global.asax:

ModelBinders.Binders.DefaultBinder = new EmptyStringModelBinder();

You can read more about this feature here.

ASP.NET Core MVC - empty string to null when sending JSON to server

After going through source code of ASP.NET Core MVC (v2.1) and source code of Newtonsoft.Json (v11.0.2), I came up with following solution.

First, create custom JsonConverter:

public class EmptyStringToNullJsonConverter : JsonConverter
{
public override bool CanRead => true;
public override bool CanWrite => false;

public override bool CanConvert(Type objectType)
{
return typeof(string) == objectType;
}

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
string value = (string)reader.Value;
return string.IsNullOrWhiteSpace(value) ? null : value.Trim();
}

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException("Unnecessary because CanWrite is false. The type will skip the converter.");
}
}

Then, register custom converter globally:

services
.AddMvc(.....)
.AddJsonOptions(options => options.SerializerSettings.Converters.Add(new EmptyStringToNullJsonConverter()))

Or, use it on per-property bases via JsonConverterAttribute. For example:

public class Item
{
public int Value { get; set; }

[StringLength(50, MinimumLength = 3)]
[JsonConverter(typeof(EmptyStringToNullJsonConverter))]
public string Description { get; set; }
}

Ajax passing empty value but Controller get null in ASP.NET MVC

I decided summary from @Rahul Sharma's and @rhytonix's answers along with giving you examples and more detailed explanations.

  1. Why is it that when in Ajax I am sending empty, I get null value in my controller?

This is simply because MVC 2.0 defaults to initializing strings to null. To be more precise, if an empty string means has no value, So .NET sets the default value of its. And the default string (belonging to reference type) is null.

More details in Model String Property Binding Breaking Change


  1. Is there a better way and more elegant to resolve my problem like below?

There are some ways to bind String property as string.Empty instead of null

1. From C# 6, You can use DefaultValueAttribute to have auto-property an initial value like below

public string LastName => string.Empty; 

Basically, This way is the same as the OP's solution mentioned in the post, Just more elegant.

2. Custom default implementation of IModelBinder by inheriting from DefaultModelBinder and changing the ConvertEmptyStringToNull value to false on the internal ModelMetaData object.

public sealed class EmptyStringModelBinder : DefaultModelBinder 
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
bindingContext.ModelMetadata.ConvertEmptyStringToNull = false;
return base.BindModel(controllerContext, bindingContext);
}
}

Then in Application_Start() method of Global.asax.cs you need to do like below to complete

protected void Application_Start()
{
ModelBinders.Binders.DefaultBinder = new EmptyStringModelBinder();
RegisterRoutes( RouteTable.Routes );
}

3. Use DisplayFormatAttribute.ConvertEmptyStringToNull Property like below

[DisplayFormat(ConvertEmptyStringToNull = false)]
public string LastName { get; set; }

Simply because in ModelMetadata

true if empty string values are automatically converted to null;
otherwise, false. The default is true

How to resolve null json string passed to mvc controller?

Your controller needs to be created to accept the actual object, and not the json string.

By this i mean

[HttpPost]
public ActionResult updateStatus(stirng jsonString)
{
//deserialise the json to a Status object here
}

Should be

[HttpPost]
public ActionResult updateStatus(Status vm)
{
//no need to deserialize - was already done by the model binder
}

In order for the model binder to bind your json to Status you would need to pass a json object that replicates your viewmodel.

{
ID:yourId,
Contact_Email:yourContactEmail,
RID:YourRID,
Name:yourName
}

In pseudo-code, you could:

var statusData = {
ID:tableData[0],
Contact_Email:tableData[1],
RID:tableData[2],
Name:tableData[3]
};

Then in your ajax call,

data: JSON.stringify(statusData),

JSON object with non-null values converted to null in MVC Controller method

I figured this out. In my C# object that represents the fields you see in the picture, I converted all to properties. Or in other words, I added "{ get; set; }" to all the fields that did not have it. Now all the values bind correctly when sent to my MVC Controller method.

Class member is json-serialized as empty string instead of null

Mathias R. Jessen's helpful answer explains the problem well: if you assign $null to a [string] typed property or variable, you'll end up with '' (the empty string).

There is an - obscure - workaround, which assumes that you have control over the values you assign:

You can assign [NullString]::Value to a [string]-type-constrained variable or property to make it store a $null value:

Class foo {
# Default to $null
[string] $X = [NullString]::Value
}

[foo]::new() | ConvertTo-Json

[foo] @{ X = [NullString]::Value } | ConvertTo-Json

($o = New-Object foo).X = [NullString]::Value
$o | ConvertTo-Json

Note:

  • The primary purpose of [NullString]::Value is to allow passing true null ($null) values to string-typed parameters of .NET Methods, which wouldn't work when passing [string]-typed PowerShell variables or properties, which on assignment invariably convert $null to '' (the empty string), as explained in Mathias' answer.

  • While you can use it in pure PowerShell code as well, as shown above, be mindful that other code that accepts [string]-typed values may not expect them to ever be $null, and that passing a value to another [string]-constrained (parameter) variable again converts to ''.

  • See this answer for more information.

The above yields:

{
"X": null
}
{
"X": null
}
{
"X": null
}

How can I convert an empty string value to a nullable date value?

Well, assuming that you have control of the API and the models on that end, you could write a custom JsonConverter<DateTime?> that handles empty strings by returning null.

A simple implementation of the JsonConverter<DateTime?> might look something like this...

using System;
using System.Text.Json;
using System.Text.Json.Serialization;

public class NullableDateTimeConverter : JsonConverter<DateTime?>
{
public override DateTime? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var @string = reader.GetString();
if (string.IsNullOrWhiteSpace(@string))
{
return null;
}
return DateTime.Parse(@string);
}

public override void Write(Utf8JsonWriter writer, DateTime? value, JsonSerializerOptions options)
{
if (value != null)
{
writer.WriteStringValue(value.Value.ToString("o"));
}
}
}

Then, you can tell your model to use it with a JsonConverterAttribute.

using System;
using System.Test.Json.Serialization;

public class TheModel
{
[JsonConverter(typeof(NullableDateTimeConverter))]
public DateTime? ExtractionDate { get; set; }
}


Related Topics



Leave a reply



Submit