Using Serializable attribute on Model in WebAPI - c#

I have the following scenario: I am using WebAPI and returning JSON results to the consumer based on a model. I now have the additional requirement to serialize the models to base64 to be able to persist them in cache and/or use them for auditing purposes. Problem is that when I add the [Serializable] attribute to the model so for converting the the model to Base64, the JSON output changes as follows:
The Model:
[Serializable]
public class ResortModel
{
public int ResortKey { get; set; }
public string ResortName { get; set; }
}
Without the [Serializable] attribute the JSON output is:
{
"ResortKey": 1,
"ResortName": "Resort A"
}
With the [Serializable] attribute the JSON output is:
{
"<ResortKey>k__BackingField": 1,
"<ResortName>k__BackingField": "Resort A"
}
How would I be able to use the [Serializable] attribute without changing the output of the JSON?

By default, Json.NET ignores the Serializable attribute. However, according to a comment to this answer by Maggie Ying (quoted below because comments are not meant to last), WebAPI overrides that behavior, which causes your output.
Json.NET serializer by default set the IgnoreSerializableAttribute to true. In WebAPI, we set that to false. The reason why you hit this issue is because Json.NET ignores properties: "Json.NET now detects types that have the SerializableAttribute and serializes all the fields on that type, both public and private, and ignores the properties" (quoted from james.newtonking.com/archive/2012/04/11/…)
A simple example that demonstrates the same behavior without WebAPI can look like this:
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System;
namespace Scratch
{
[Serializable]
class Foo
{
public string Bar { get; set; }
}
class Program
{
static void Main()
{
var foo = new Foo() { Bar = "Blah" };
Console.WriteLine(JsonConvert.SerializeObject(foo, new JsonSerializerSettings()
{
ContractResolver = new DefaultContractResolver()
{
IgnoreSerializableAttribute = false
}
}));
}
}
}
There are several ways around this behavior. One is to decorate your model with a plain JsonObject attribute:
[Serializable]
[JsonObject]
class Foo
{
public string Bar { get; set; }
}
Another way is to override the default settings in your Application_Start(). According to this answer, the default settings should do it:
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings = new Newtonsoft.Json.JsonSerializerSettings();
If that doesn't work, you could be explicit about it:
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings = new JsonSerializerSettings()
{
ContractResolver = new DefaultContractResolver()
{
IgnoreSerializableAttribute = true
}
};

Related

JsonMediaTypeFormatter not formatting data correclty

I am using following code in ASP.NET Web API application.
//Support camel casing
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().FirstOrDefault();
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
When returning JSON via POCO or DataTable, it converts property name in camel casing.
Assume My class has two properties.
Class Obj{
public string DataBase{ get; set; }
public string ChangedBy { get; set; }
}
When I return any object of this class, I will get JSON like this:
{
"dataBase":"Oracle",
"changedBy":"XYZ"
}
It seems issue is when you have '_' in the property name. CamelCasing is not making sense.
My class has columns like this:
DATA_BASE
CHANGED_BY
Now, I am receiving JSON like this:
{
"datA_BASE":"Oracle",
"changeD_BY":"XYZ"
}
I was expecting:
{
"dATA_BASE":"Oracle",
"cHANGED_BY":"XYZ"
}

Avoid allowing any case for property names in .NET core rest controller

I am creating a REST controller with .NET core 2.1 using [ApiController] and [FromBody]. Suppose my parameter object is:
public class CreateUserParmeters
{
public string Name {get; set;}
}
The JSON I can send can be:
{ "name":"Test" }
But also:
{ "Name":"Test" }
Or even:
{ "NaMe":"Test" }
This will all work fine. I would like to avoid this, and only allow name (so camelCase). Is there a way to enforce this?
Maybe this setting will help:
services.AddMvc().AddJsonOptions(opt =>
{
opt.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
});
Have you tried this?
I think you should investigate the following contract resolver.
In your Global.asax:
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
You could simply define the required json attribute name using the JsonProperty attribute on the model properties. It will serialise as you require, although it's not actually case sensitive when de-serialising json back to a model instance.
[JsonProperty("name")]
public string Name { get; set; }

Newtonsoft Json serializer not using property name overrides defined by JsonPropertyAttribute

I have a simple class like so, which I'd like to serialize and send to the client in camel case notation.
I've declared the class in C# honoring the conventional Pascal case notation.
I've also set the JsonProperty attribute on each property with a name override as follows.
using Newtonsoft.Json;
namespace Code.ViewModels
{
public class ArticleCategoryListItem
{
[JsonProperty(PropertyName = "value")]
public string Value { get; set; }
[JsonProperty(PropertyName = "label")]
public string Label { get; set; }
}
}
However, my client still receives Pascal case property names.
I've tried clearing the ASP.NET cache, cleaning the build and rebuilding the solution and restarting Visual Studio, all to no avail. What's going on?
As far as I can tell from the source code, JSON.Net isn't used by JsonResult.
It instead uses the JavaScriptSerializer.
I'm not 100% sure that the version you're using doesn't use JSON.Net, but if it doesn't then that obviously would explain why the attributes aren't being honored.
I have previously used a JsonDotNetResult in situations like this.
You have to set JsonSerializationSetting for
camel case ContractResolver = new CamelCasePropertyNamesContractResolver()
and use like
JsonConvert.SerializeObject(object, new JsonSerializerSettings
{ ContractResolver = new CamelCasePropertyNamesContractResolver() });

JSON.NET Case Insensitive Deserialization not working

I need to deserialize some JSON into my object where the casing of the JSON is unknown/inconsistent. JSON.NET is supposed to be case insensitive but it not working for me.
My class definition:
public class MyRootNode
{
public string Action {get;set;}
public MyData Data {get;set;}
}
public class MyData
{
public string Name {get;set;}
}
The JSON I receive has Action & Data in lowercase and has the correct casing for MyRootNode.
I'm using this to deserialize:
MyRootNode ResponseObject = JsonConvert.DeserializeObject<MyRootnode>(JsonString);
It returns to be an initialised MyRootNode but the Action and Data properties are null.
Any ideas?
EDIT: Added JSON
{
"MyRootNode":{
"action":"PACT",
"myData":{
"name":"jimmy"
}
}
}
This is the .NET Core built-in JSON library.
I found another way of doing it.. just in case, somebody is still looking for a cleaner way of doing it. Assume there exists a Movie class
using System.Text.Json;
.
.
.
var movies = await JsonSerializer.DeserializeAsync
<IEnumerable<Movie>>(responseStream,
new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
Startup Options:
You can also configure at the time of application startup using the below extension method.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers()
.AddJsonOptions(
x =>
{
x.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
});
}
Simply add JsonProperty attribute and set jsonProperty name
public class MyRootNode
{
[JsonProperty(PropertyName = "action")]
public string Action {get;set;}
[JsonProperty(PropertyName = "myData")]
public MyData Data {get;set;}
}
public class MyData
{
[JsonProperty(PropertyName = "name")]
public string Name {get;set;}
}
UPD: and yes, add some base type as #mjwills suggest
You need to add an additional class:
public class MyRootNodeWrapper
{
public MyRootNode MyRootNode {get;set;}
}
and then use:
MyRootNodeWrapperResponseObject = JsonConvert.DeserializeObject<MyRootNodeWrapper>(JsonString);
https://stackoverflow.com/a/45384366/34092 may be worth a read. It is basically the same scenario.
Also, change:
public MyData Data {get;set;}
to:
public MyData MyData {get;set;}
as per advice from #demo and #Guy .

How to ignore a property in class if null, using json.net

I am using Json.NET to serialize a class to JSON.
I have the class like this:
class Test1
{
[JsonProperty("id")]
public string ID { get; set; }
[JsonProperty("label")]
public string Label { get; set; }
[JsonProperty("url")]
public string URL { get; set; }
[JsonProperty("item")]
public List<Test2> Test2List { get; set; }
}
I want to add a JsonIgnore() attribute to Test2List property only when Test2List is null. If it is not null then I want to include it in my json.
An alternate solution using the JsonProperty attribute:
[JsonProperty(NullValueHandling=NullValueHandling.Ignore)]
// or
[JsonProperty("property_name", NullValueHandling=NullValueHandling.Ignore)]
// or for all properties in a class
[JsonObject(ItemNullValueHandling = NullValueHandling.Ignore)]
As seen in this online doc.
As per James Newton King: If you create the serializer yourself rather than using JavaScriptConvert there is a NullValueHandling property which you can set to ignore.
Here's a sample:
JsonSerializer _jsonWriter = new JsonSerializer {
NullValueHandling = NullValueHandling.Ignore
};
Alternatively, as suggested by #amit
JsonConvert.SerializeObject(myObject,
Newtonsoft.Json.Formatting.None,
new JsonSerializerSettings {
NullValueHandling = NullValueHandling.Ignore
});
JSON.NET also respects the EmitDefaultValue property on DataMemberAttribute, in case you don't want to add Newtonsoft-specific attributes to your model:
[DataMember(Name="property_name", EmitDefaultValue=false)]
You can write: [JsonProperty("property_name",DefaultValueHandling = DefaultValueHandling.Ignore)]
It also takes care of not serializing properties with default values (not only null). It can be useful for enums for example.
You can do this to ignore all nulls in an object you're serializing, and any null properties won't then appear in the JSON
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.NullValueHandling = NullValueHandling.Ignore;
var myJson = JsonConvert.SerializeObject(myObject, settings);
In my case, using .NET 6 this was the solution:
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
More info here.
As can be seen in this link on their site (http://james.newtonking.com/archive/2009/10/23/efficient-json-with-json-net-reducing-serialized-json-size.aspx) I support using [Default()] to specify default values
Taken from the link
public class Invoice
{
public string Company { get; set; }
public decimal Amount { get; set; }
// false is default value of bool
public bool Paid { get; set; }
// null is default value of nullable
public DateTime? PaidDate { get; set; }
// customize default values
[DefaultValue(30)]
public int FollowUpDays { get; set; }
[DefaultValue("")]
public string FollowUpEmailAddress { get; set; }
}
Invoice invoice = new Invoice
{
Company = "Acme Ltd.",
Amount = 50.0m,
Paid = false,
FollowUpDays = 30,
FollowUpEmailAddress = string.Empty,
PaidDate = null
};
string included = JsonConvert.SerializeObject(invoice,
Formatting.Indented,
new JsonSerializerSettings { });
// {
// "Company": "Acme Ltd.",
// "Amount": 50.0,
// "Paid": false,
// "PaidDate": null,
// "FollowUpDays": 30,
// "FollowUpEmailAddress": ""
// }
string ignored = JsonConvert.SerializeObject(invoice,
Formatting.Indented,
new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Ignore });
// {
// "Company": "Acme Ltd.",
// "Amount": 50.0
// }
In .Net Core this is much easier now. In your startup.cs just add json options and you can configure the settings there.
public void ConfigureServices(IServiceCollection services)
....
services.AddMvc().AddJsonOptions(options =>
{
options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
});
With Json.NET
public class Movie
{
public string Name { get; set; }
public string Description { get; set; }
public string Classification { get; set; }
public string Studio { get; set; }
public DateTime? ReleaseDate { get; set; }
public List<string> ReleaseCountries { get; set; }
}
Movie movie = new Movie();
movie.Name = "Bad Boys III";
movie.Description = "It's no Bad Boys";
string ignored = JsonConvert.SerializeObject(movie,
Formatting.Indented,
new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
The result will be:
{
"Name": "Bad Boys III",
"Description": "It's no Bad Boys"
}
With System.Text.Json and .NET Core 3.0 this worked for me:
var jsonSerializerOptions = new JsonSerializerOptions()
{
IgnoreNullValues = true
};
var myJson = JsonSerializer.Serialize(myObject, jsonSerializerOptions );
An adaption to #Mrchief's / #amit's answer, but for people using VB
Dim JSONOut As String = JsonConvert.SerializeObject(
myContainerObject,
New JsonSerializerSettings With {
.NullValueHandling = NullValueHandling.Ignore
}
)
See:
"Object Initializers: Named and Anonymous Types (Visual Basic)"
https://msdn.microsoft.com/en-us/library/bb385125.aspx
Or just by setting like this.
services.AddMvc().AddJsonOptions(options =>
options.JsonSerializerOptions.IgnoreNullValues = true;
});
To expound slightly on GlennG's very helpful answer (translating the syntax from C# to VB.Net is not always "obvious") you can also decorate individual class properties to manage how null values are handled. If you do this don't use the global JsonSerializerSettings from GlennG's suggestion, otherwise it will override the individual decorations. This comes in handy if you want a null item to appear in the JSON so the consumer doesn't have to do any special handling. If, for example, the consumer needs to know an array of optional items is normally available, but is currently empty...
The decoration in the property declaration looks like this:
<JsonPropertyAttribute("MyProperty", DefaultValueHandling:=NullValueHandling.Include)> Public Property MyProperty As New List(of String)
For those properties you don't want to have appear at all in the JSON change :=NullValueHandling.Include to :=NullValueHandling.Ignore.
By the way - I've found that you can decorate a property for both XML and JSON serialization just fine (just put them right next to each other). This gives me the option to call the XML serializer in dotnet or the NewtonSoft serializer at will - both work side-by-side and my customers have the option to work with XML or JSON. This is slick as snot on a doorknob since I have customers that require both!
Here's an option that's similar, but provides another choice:
public class DefaultJsonSerializer : JsonSerializerSettings
{
public DefaultJsonSerializer()
{
NullValueHandling = NullValueHandling.Ignore;
}
}
Then, I use it like this:
JsonConvert.SerializeObject(postObj, new DefaultJsonSerializer());
The difference here is that:
Reduces repeated code by instantiating and configuring JsonSerializerSettings each place it's used.
Saves time in configuring every property of every object to be serialized.
Still gives other developers flexibility in serialization options, rather than having the property explicitly specified on a reusable object.
My use-case is that the code is a 3rd party library and I don't want to force serialization options on developers who would want to reuse my classes.
Potential drawbacks are that it's another object that other developers would need to know about, or if your application is small and this approach wouldn't matter for a single serialization.
This does not exactly answer the original question, but may prove useful depending on the use case. (And since I wound up here after my search, it may be useful for others.)
In my most recent experience, I'm working with a PATCH api. If a property is specified but with no value given (null/undefined because it's js), then the property and value are removed from the object being patched. So I was looking for a way to selectively build an object that could be serialized in such a way that this would work.
I remembered seeing the ExpandoObject, but never had a true use case for it until today. This allows you to build an object dynamically, so you won't have null properties unless you want them there.
Here is a working fiddle, with the code below.
Results:
Standard class serialization
noName: {"Name":null,"Company":"Acme"}
noCompany: {"Name":"Fred Foo","Company":null}
defaultEmpty: {"Name":null,"Company":null}
ExpandoObject serialization
noName: {"Company":"Acme"}
noCompany: {"name":"Fred Foo"}
defaultEmpty: {}
Code:
using Newtonsoft.Json;
using System;
using System.Dynamic;
public class Program
{
public static void Main()
{
SampleObject noName = new SampleObject() { Company = "Acme" };
SampleObject noCompany = new SampleObject() { Name = "Fred Foo" };
SampleObject defaultEmpty = new SampleObject();
Console.WriteLine("Standard class serialization");
Console.WriteLine($" noName: { JsonConvert.SerializeObject(noName) }");
Console.WriteLine($" noCompany: { JsonConvert.SerializeObject(noCompany) }");
Console.WriteLine($" defaultEmpty: { JsonConvert.SerializeObject(defaultEmpty) }");
Console.WriteLine("ExpandoObject serialization");
Console.WriteLine($" noName: { JsonConvert.SerializeObject(noName.CreateDynamicForPatch()) }");
Console.WriteLine($" noCompany: { JsonConvert.SerializeObject(noCompany.CreateDynamicForPatch()) }");
Console.WriteLine($" defaultEmpty: { JsonConvert.SerializeObject(defaultEmpty.CreateDynamicForPatch()) }");
}
}
public class SampleObject {
public string Name { get; set; }
public string Company { get; set; }
public object CreateDynamicForPatch()
{
dynamic x = new ExpandoObject();
if (!string.IsNullOrWhiteSpace(Name))
{
x.name = Name;
}
if (!string.IsNullOrEmpty(Company))
{
x.Company = Company;
}
return x;
}
}
.Net 6 -
Add the code in Program.cs. This will ignore the class or record property if it is null.
using System.Text.Json.Serialization;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers()
.AddJsonOptions(opts =>
{
var enumConverter = new JsonStringEnumConverter();
opts.JsonSerializerOptions.Converters.Add(enumConverter);
opts.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault | JsonIgnoreCondition.WhenWritingNull;
});
var settings = new JsonSerializerSettings();
settings.ContractResolver = new CamelCasePropertyNamesContractResolver();
settings.NullValueHandling = NullValueHandling.Ignore;
//you can add multiple settings and then use it
var bodyAsJson = JsonConvert.SerializeObject(body, Formatting.Indented, settings);

Categories