Newtonsoft Json serializer not using property name overrides defined by JsonPropertyAttribute - c#

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() });

Related

Azure Search v11: Indexing nullable Collection of Complex Type

I'm updating the SDK for the Azure Cognitive Search service, from v10 to v11. I have followed all the steps in the guide in order to upgrade, however I have noticed a strange behavior about the indexing (merge or upload) operation: the UploadDocumentAsync (but also with other methods used to indexing data) operation fails when a property of type Collection (Edm.ComplexType) is null, with the following error:
A node of type 'PrimitiveValue' was read from the JSON reader when trying to read the contents of the property. However, a 'StartArray' node was expected json.
IndexDocumentsResult response = await searchClient.UploadDocumentsAsync<T>(documents).ConfigureAwait (false);
With v10 this problem did not arise. A workaround I found is to set collections as empty arrays and not with null value, but I would like to find a better solution.
EDITED:
I upgraded from Microsoft.Azure.Search v10.1.0 to Azure.Search.Documents v11.1.1
Following an example of a generic T class used to indexing data:
public class IndexEntity
{
[JsonProperty("#search.score")]
public double SearchScore { get; set; }
[JsonProperty("Key")]
public Guid Id { get; set; }
[JsonProperty("Code")]
public string Code { get; set; }
[JsonProperty("ComplexObj")]
public ComplexType[] CollectionOfComplexType{ get; set; }
}
Following the definition of ModelObjectToIndex
public class ComplexType
{
[JsonProperty("Id")]
public string Id { get; set; }
[JsonProperty("Value")]
public string Value { get; set; }
}
Basically when the CollectionOfComplexType property is null, I get the above error. If I set it as an empty array, the error does not occur, but as mentioned I don't like this solution, furthermore in the old version it was an allowed operation (the indexing was completed successfully)
Our Azure.Search.Documents behavior seems to have changed in this regard. I've opened https://github.com/Azure/azure-sdk-for-net/issues/18169 to track resolution.
You can workaround this issue without initializing your collections to an empty array by passing in a JsonSerializerSettings that was similar to what we did in our older Microsoft.Azure.Search library, since it seems from using the JsonPropertyAttribute you're using Newtonsoft.Json (aka Json.NET) anyway:
Add a package reference to Microsoft.Azure.Core.NewtonsoftJson if you haven't already. It recently GA'd so you don't need to use a preview if you were, which I presume since System.Text.Json - our default serializer - would not have honored your property renames.
Pass in a JsonSerializerSettings before creating your SearchClient like so:
var settings = new JsonSerializerSettings
{
// Customize anything else you want here; otherwise, defaults are used.
NullValueHandling = NullValueHandling.Ignore,
};
var options = new SearchClientOptions
{
Serializer = new NewtonsoftJsonObjectSerializer(settings),
};
var searchClient = new SearchClient(options);
We'll discuss how to resolve this by default, if we even can. One big change from the older library is the ability to customize the serializer used. By default we use System.Text.Json, but we support other serializers including Newtonsoft.Json. If someone were to pass in their own settings - or even desire the defaults - changing that could be catastrophic. So I'm curious: if we at least documented this behavior change (perhaps on the SearchClient class remarks and/or UploadDocuments and related methods) and how to retain previous behavior, would that have helped or otherwise been satisfactory?

Creating API models in .NET with PascalCase property names but serializing to CamelCase

I usually use a variety of text manipulation tools to extract a list of properties from some REST API documentation, and then use Newtonsoft.Json to add an annotation above the field in order to tell the program whilst this property may be called "DeliveryAddress" when we serialize to JSON please call it "deliveryAddress" using
[JsonProperty(PropertyName = "deliveryAddress")]
public string DeliveryAddress{ get; set; }
It seems a bit long winded so I was wondering if there was an easier way, or some feature in VS I could use to make a 'macro' of sorts to apply this annotation to a list of PascalCase properties.
Well that was easy, turns out I've been cluttering my code unnecessarily all this time.
Hopefully this will serve as a useful question for others in my position.
There is another class level annotation that can be used here.
[JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))]
public class Order
{
public string DeliveryAddress {get;set;}
public string FirstName {get;set;}
[JsonProperty(NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public string NewlyAddedProperty {get;set;}
}
This will apply the CamelCasing upon serialization to all properties, and this can be overridden at an inline annotation level as shown above.
What a lovely library.
Property names serialize to camelCase by default in ASP.net core.
If for some reason this is not the case or you need to customize it further, the naming strategy can manually be specified by setting the NamingStrategy in the JSON serializer settings:
services.AddMvc().AddJsonOptions(options =>
{
var resolver = options.SerializerSettings.ContractResolver as DefaultContractResolver;
resolver.NamingStrategy = new CamelCaseNamingStrategy();
});
Then any time you return an object from an API, it will be serialized with camel case names.
If you're manually serializing the JSON to a string, you can inject IOptions<MvcJsonOptions> to access the default serializer settings which MVC uses:
var jsonString = JsonConvert.SerializeObject(obj, options.Value.SerializerSettings);
You can manually build a serializer with a case converter:
var jsonSerializersettings = new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
var myJsonOutput = JsonConvert.DeserializeObject<object>myJsonInput.ToString(),jsonSerializersettings);

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; }

JSON Serialization of URI Type in WebAPI 2 ODATA 3 C# Project

I'm seeing C# URI types serialized to JSON in an ODATA 3 controller in my WebAPI 2 project as an array of segments that does not include the domain. I've tried everything I can think of to change this (including fiddling with the serialization settings and even trying out the contract serializer instead of JSON.Net). Nothing seems to change the behavior. Note, I am not using .Net Core. Here is a code sample, condensed into a single snippet.
namespace WebApplication1.Controllers
{
public class MyObject
{
public Uri Url { get; set; }
public string Name { get; set; }
public string ID { get; set; }
}
public class MyObjectsController : ODataController
{
private static ODataValidationSettings _validationSettings = new ODataValidationSettings();
public IHttpActionResult GetMyObjects(ODataQueryOptions<MyObject> queryOptions)
{
try
{
queryOptions.Validate(_validationSettings);
return Ok<IEnumerable<MyObject>>(new List<MyObject>() { new MyObject() { ID="asdf", Name="123rwe", Url = new Uri("http://www.webapp.com/sites/page.html") } });
}
catch (ODataException ex)
{
return BadRequest(ex.Message);
}
}
}
}
This generates the following JSON in the browser:
{
"odata.metadata":"http://localhost:51607/odata/$metadata#MyObjects","value":[
{
"Url":{
"Segments":[
"/","sites/","page.html"
]
},"Name":"123rwe","ID":"asdf"
}
]
}
This is what I'd like (without changing the Url property to a string):
{
"odata.metadata":"http://localhost:51607/odata/$metadata#MyObjects","value":[
{
"Url":"http://www.webapp.com/sites/page.html","Name":"123rwe","ID":"asdf"
}
]
}
Any thoughts?
UPDATE:
Further research is suggesting that serialization behavior for URI types in WebAPI ODATA is controlled by the odataentityreferencelink and odataentityreferencelinkserializer classes. Specifically, URI type appear to be converted to odataentityreferencelink types which are then serialized in the manner I posted above (as an array of segments not including the root domain). I still need to know how to change this behavior, however the documentation for these two classes is not proving helpful. Last, I've confirmed this problem is not specific to the JSON output format. The serialization behavior for both XML/Atom and JSON are identical: URIs are broken down into an array of segments.
MS Premier support provided a final answer to this which I'll share below.
There is no option to directly JSON serialize an URI type, normally it would be broken into array of segments as you are observing in your code
The domain name will be eliminated as a normal scenario
The option you can go for is to create a custom URI Converter deriving from JSONConverter which is a part of Newtonsoft.Json namespace

Using Serializable attribute on Model in WebAPI

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
}
};

Categories