I have a Web Api project being configured like this:
config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
However, I want dictionary keys casing to remain unchanged. is there any attribute in Newtonsoft.Json I can use to a class to denote that I want casing to remain unchanged during serialization?
public class SomeViewModel
{
public Dictionary<string, string> Data { get; set; }
}
There is not an attribute to do this, but you can do it by customizing the resolver.
I see that you are already using a CamelCasePropertyNamesContractResolver. If you derive a new resolver class from that and override the CreateDictionaryContract() method, you can provide a substitute DictionaryKeyResolver function that does not change the key names.
Here is the code you would need:
class CamelCaseExceptDictionaryKeysResolver : CamelCasePropertyNamesContractResolver
{
protected override JsonDictionaryContract CreateDictionaryContract(Type objectType)
{
JsonDictionaryContract contract = base.CreateDictionaryContract(objectType);
contract.DictionaryKeyResolver = propertyName => propertyName;
return contract;
}
}
Demo:
class Program
{
static void Main(string[] args)
{
Foo foo = new Foo
{
AnIntegerProperty = 42,
HTMLString = "<html></html>",
Dictionary = new Dictionary<string, string>
{
{ "WHIZbang", "1" },
{ "FOO", "2" },
{ "Bar", "3" },
}
};
JsonSerializerSettings settings = new JsonSerializerSettings
{
ContractResolver = new CamelCaseExceptDictionaryKeysResolver(),
Formatting = Formatting.Indented
};
string json = JsonConvert.SerializeObject(foo, settings);
Console.WriteLine(json);
}
}
class Foo
{
public int AnIntegerProperty { get; set; }
public string HTMLString { get; set; }
public Dictionary<string, string> Dictionary { get; set; }
}
Here is the output from the above. Notice that all of the class property names are camel-cased, but the dictionary keys have retained their original casing.
{
"anIntegerProperty": 42,
"htmlString": "<html></html>",
"dictionary": {
"WHIZbang": "1",
"FOO": "2",
"Bar": "3"
}
}
Json.NET 9.0.1 introduced the NamingStrategy class hierarchy to handle this sort of issue. It extracts the logic for algorithmic remapping of property names from the contract resolver to a separate, lightweight class that allows for control of whether dictionary keys, explicitly specified property names, and extension data names (in 10.0.1) are remapped.
By using DefaultContractResolver and setting NamingStrategy to an instance of CamelCaseNamingStrategy you can generate JSON with camel-cased property names and unmodified dictionary keys by setting it in JsonSerializerSettings.ContractResolver:
var resolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy
{
ProcessDictionaryKeys = false,
OverrideSpecifiedNames = true
}
};
config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = resolver;
Notes:
The current implementation of CamelCasePropertyNamesContractResolver also specifies that .Net members with explicitly specified property names (e.g. ones where JsonPropertyAttribute.PropertyName has been set) should have their names remapped:
public CamelCasePropertyNamesContractResolver()
{
NamingStrategy = new CamelCaseNamingStrategy
{
ProcessDictionaryKeys = true,
OverrideSpecifiedNames = true
};
}
The above resolver preserves this behavior. If you don't want this, set OverrideSpecifiedNames = false.
Json.NET has several built-in naming strategies including:
CamelCaseNamingStrategy. A camel case naming strategy that contains the name-remapping logic formerly embedded in CamelCasePropertyNamesContractResolver.
SnakeCaseNamingStrategy. A snake case naming strategy.
DefaultNamingStrategy. The default naming strategy. Property names and dictionary keys are unchanged.
Or, you can create your own by inheriting from the abstract base class NamingStrategy.
While it is also possible to modify the NamingStrategy of an instance of CamelCasePropertyNamesContractResolver, since the latter shares contract information globally across all instances of each type, this can lead to unexpected side-effects if your application tries to use multiple instances of CamelCasePropertyNamesContractResolver. No such problem exists with DefaultContractResolver, so it is safer to use when any customization of casing logic is required.
That is a very nice answer. But why not just override the ResolveDictionaryKey?
class CamelCaseExceptDictionaryResolver : CamelCasePropertyNamesContractResolver
{
#region Overrides of DefaultContractResolver
protected override string ResolveDictionaryKey(string dictionaryKey)
{
return dictionaryKey;
}
#endregion
}
The selected answer is perfect but I guess by the time I'm typing this, the contract resolver must change to something like this because DictionaryKeyResolver doesn't exists anymore :)
public class CamelCaseExceptDictionaryKeysResolver : CamelCasePropertyNamesContractResolver
{
protected override JsonDictionaryContract CreateDictionaryContract(Type objectType)
{
JsonDictionaryContract contract = base.CreateDictionaryContract(objectType);
contract.PropertyNameResolver = propertyName => propertyName;
return contract;
}
}
Related
I would like the following Author type to have a default JsonConverter, and be able to override it at runtime.
[JsonConverter(typeof(BaseJsonConverter))]
public class Author
{
// The ID of an author entity in the application.
public int ID { set; get; }
// The ID of an Author entity in its source.
public string SourceID { set; set; }
}
I used the following code to override the default converter (i.e., BaseJsonConverter).
public class AlternativeConverter : BaseJsonConverter
{ // the serializer implementation is removed for clarity. }
// Deserialize using AlternativeConverter:
var author = JsonConvert.DeserializeObject<Author>(jsonString, new AlternativeConverter());
Question
Using the above call, the AlternativeConverter is first constructed; however, then an instance of BaseJsonConverter is initialized and used for deserialization. So, the AlternativeConverter is never used.
Executable example: https://dotnetfiddle.net/l0bgYO
Use case
The application is to convert different JSON objects, obtained from different sources, to a common C# type. Commonly data comes from a source for that we define the default converter (i.e., BaseJsonConverter), and for data coming from other sources, we define different converters per each.
Background
I am aware of methods such as this one, and indeed I am using similar method partially. With ref to that article, I need to have different _propertyMappings depending on the source of input, because in my application attribute to property mapping is not one-to-one. For instance, I have the following JSON objects:
{
"id":123
}
// and
{
"id":"456"
}
where the first JSON object should be deserialized to:
author.ID = 123
author.SourceID = null
and the second JSON object should be deserialized as:
author.ID = 0
author.SourceID = "456"
You can use a custom ContractResolver to override a [JsonConverter] attribute programmatically. To solve your problem you could make a custom resolver like this:
public class CustomResolver : DefaultContractResolver
{
private Dictionary<Type, JsonConverter> Converters { get; set; }
public CustomResolver(Dictionary<Type, JsonConverter> converters)
{
Converters = converters;
}
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
JsonObjectContract contract = base.CreateObjectContract(objectType);
if (Converters.TryGetValue(objectType, out JsonConverter converter))
{
contract.Converter = converter;
}
return contract;
}
}
Then, when you wanted to use the AlternativeConverter in place of the BaseJsonConverter, you could use the custom resolver like this:
// map the `Author` type to the `AlternativeConverter`
var converters = new Dictionary<Type, JsonConverter>()
{
{ typeof(Author), new AlternativeConverter() }
};
// Create a resolver with the converter mapping and add it to the serializer settings
var settings = new JsonSerializerSettings
{
ContractResolver = new CustomResolver(converters)
};
// Use the settings when deserializing
var author = JsonConvert.DeserializeObject<Author>(jsonString, settings);
Demo Fiddle: https://dotnetfiddle.net/cu0igV
Of course, if all you're really doing with these converters is remapping properties to different names, you could just use a ContractResolver for that in the first place and get rid of the converters altogether. See Json.NET deserialize or serialize json string and map properties to different property names defined at runtime for more information on that approach.
I think you should try to use different JsonSerializerSettings instances for different data sources, with different Converters collections. And remove JsonConverter attributes from your classes.
In the new System.Text.Json; namespace is there something like IContractResolver i am trying to migrate my project away from Newtonsoft.
This is one of the classes i am trying to move:
public class SelectiveSerializer : DefaultContractResolver
{
private readonly string[] fields;
public SelectiveSerializer(string fields)
{
var fieldColl = fields.Split(',');
this.fields = fieldColl
.Select(f => f.ToLower().Trim())
.ToArray();
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
property.ShouldSerialize = o => fields.Contains(member.Name.ToLower());
return property;
}
}
The equivalent types in System.Text.Json -- JsonClassInfo and JsonPropertyInfo -- are internal. There is an open enhancement
Equivalent of DefaultContractResolver in System.Text.Json #31257
asking for a public equivalent. – dbc Nov 25 at 19:11
Github issues:
Open up metadata infrastructure of System.Text.Json #34456
Equivalent of DefaultContractResolver.CreateProperties override in System.Text.Json #60518
Equivalent of DefaultContractResolver in System.Text.Json #31257
Please try this:
I wrote this as an extension to System.Text.Json to offer missing features: https://github.com/dahomey-technologies/Dahomey.Json.
You will find support for programmatic object mapping.
Define your own implementation of IObjectMappingConvention:
public class SelectiveSerializer : IObjectMappingConvention
{
private readonly IObjectMappingConvention defaultObjectMappingConvention = new DefaultObjectMappingConvention();
private readonly string[] fields;
public SelectiveSerializer(string fields)
{
var fieldColl = fields.Split(',');
this.fields = fieldColl
.Select(f => f.ToLower().Trim())
.ToArray();
}
public void Apply<T>(JsonSerializerOptions options, ObjectMapping<T> objectMapping) where T : class
{
defaultObjectMappingConvention.Apply<T>(options, objectMapping);
foreach (IMemberMapping memberMapping in objectMapping.MemberMappings)
{
if (memberMapping is MemberMapping<T> member)
{
member.SetShouldSerializeMethod(o => fields.Contains(member.MemberName.ToLower()));
}
}
}
}
Define your class:
public class Employee
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
}
Setup json extensions by calling on JsonSerializerOptions the extension method SetupExtensions defined in the namespace Dahomey.Json:
JsonSerializerOptions options = new JsonSerializerOptions();
options.SetupExtensions();
Register the new object mapping convention for the class:
options.GetObjectMappingConventionRegistry().RegisterConvention(
typeof(Employee), new SelectiveSerializer("FirstName,Email,Id"));
Then serialize your class with the regular Sytem.Text.Json API:
Employee employee = new Employee
{
Id = 12,
FirstName = "John",
LastName = "Doe",
Email = "john.doe#acme.com"
};
string json = JsonSerializer.Serialize(employee, options);
// {"Id":12,"FirstName":"John","Email":"john.doe#acme.com"};
Contract customization will be implemented in .NET 7, and is available in Preview 6.
From the documentation page What’s new in System.Text.Json in .NET 7: Contract Customization by Eirik Tsarpalis, Krzysztof Wicher and Layomi Akinrinade:
The contract metadata for a given type T is represented using JsonTypeInfo<T>, which in previous versions served as an opaque token used exclusively in source generator APIs. Starting in .NET 7, most facets of the JsonTypeInfo contract metadata have been exposed and made user-modifiable. Contract customization allows users to write their own JSON contract resolution logic using implementations of the IJsonTypeInfoResolver interface:
public interface IJsonTypeInfoResolver
{
JsonTypeInfo? GetTypeInfo(Type type, JsonSerializerOptions options);
}
A contract resolver returns a configured JsonTypeInfo instance for the given Type and JsonSerializerOptions combination. It can return null if the resolver does not support metadata for the specified input type.
Contract resolution performed by the default, reflection-based serializer is now exposed via the DefaultJsonTypeInfoResolver class, which implements IJsonTypeInfoResolver.
Starting from .NET 7 the JsonSerializerContext class used in source generation also implements IJsonTypeInfoResolver.
You can create your own IJsonTypeInfoResolver via one of the following methods:
You can subclass DefaultJsonTypeInfoResolver and override GetTypeInfo(Type, JsonSerializerOptions). This resembles overriding Json.NET's DefaultContractResolver.CreateContract().
You can add an Action<JsonTypeInfo> to DefaultJsonTypeInfoResolver.Modifiers to modify the default JsonTypeInfo generated for selected types after creation.
Combining multiple customizations looks easier with this approach than with the inheritance approach. However, since the modifier actions are applied in order, there is a chance that later modifiers could conflict with earlier modifiers.
You could create your own IJsonTypeInfoResolver from scratch that creates contracts only for those types that interest you, and combine it with some other type info resolver via JsonTypeInfoResolver.Combine(IJsonTypeInfoResolver[]).
JsonTypeInfoResolver.Combine() is also useful when you want to use compile-time generated JsonSerializerContext instances with a runtime contract resolver that customizes serialization for certain types only.
Once you have a custom resolver, you can set it via JsonSerializerOptions.TypeInfoResolver.
Thus your SelectiveSerializer can be converted to a DefaultJsonTypeInfoResolver roughly as follows, using modifiers. First define the following fluent extension methods:
public static partial class JsonSerializerExtensions
{
public static DefaultJsonTypeInfoResolver SerializeSelectedFields(this DefaultJsonTypeInfoResolver resolver, string fields) =>
SerializeSelectedFields(resolver, fields?.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) ?? throw new ArgumentNullException(nameof(fields)));
public static DefaultJsonTypeInfoResolver SerializeSelectedFields(this DefaultJsonTypeInfoResolver resolver, IEnumerable<string> membersToSerialize)
{
if (resolver == null)
throw new ArgumentNullException(nameof(resolver));
if (membersToSerialize == null)
throw new ArgumentNullException(nameof(membersToSerialize));
var membersToSerializeSet = membersToSerialize.ToHashSet(StringComparer.OrdinalIgnoreCase); // Possibly this should be changed to StringComparer.Ordinal
resolver.Modifiers.Add(typeInfo =>
{
if (typeInfo.Kind == JsonTypeInfoKind.Object)
{
foreach (var property in typeInfo.Properties)
{
if (property.GetMemberName() is {} name && !membersToSerializeSet.Contains(name))
property.ShouldSerialize = static (obj, value) => false;
}
}
});
return resolver;
}
public static string? GetMemberName(this JsonPropertyInfo property) => (property.AttributeProvider as MemberInfo)?.Name;
}
And now you can set up your JsonSerializerOptions e.g. as follows:
var options = new JsonSerializerOptions
{
TypeInfoResolver = new DefaultJsonTypeInfoResolver()
.SerializeSelectedFields("FirstName,Email,Id"),
// Add other options as required
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true,
};
Notes:
JsonPropertyInfo.ShouldSerialize (also new in .NET 7) can be used for conditional serialization of properties.
When a JsonPropertyInfo was created by the reflection or source-gen resolvers, JsonPropertyInfo.AttributeProvider will be the underlying PropertyInfo or FieldInfo.
For confirmation see this comment by layomia to System.Text.Json: In .NET 7, how can I determine the JsonPropertyInfo created for a specific member, so I can customize the serialization of that member? #77761.
All serialization metadata should be constructed using locale-invariant string logic. In your code you use ToLower() but it would have been better to use ToLowerInvariant(). In my modifier action I use StringComparer.OrdinalIgnoreCase which avoids the need to lowercase the strings.
System.Text.Json is case-sensitive by default so you might want to use case-sensitive property name matching when filtering selected fields.
I want to be able to use the CamelCasePropertyNameContractResolver but override it for specific property names. For this, I use the JsonProperty attribute. This works fine except when the name that I choose is fully uppercase. Any ideas what's wrong or how to get around it?
In the example below, Bar is serialized to "BAR" when I don't use the CamelCasePropertyNameContractResolver, but is serialized to "bar" when I do use the resolver. Foo and CamelCaseProperty are serialized correctly in both scenarios.
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
namespace ConsoleTester
{
class Program
{
static void Main(string[] args)
{
var foo = new FooBar {CamelCaseProperty = "test", Foo = "test", Bar = "test" };
var output = JsonConvert.SerializeObject(foo);
// output "CamelCaseProperty", "fOO", "BAR"
var output2 = JsonConvert.SerializeObject(foo, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() });
// output "camelCaseProperty", "fOO", "bar"
}
}
public class FooBar
{
public string CamelCaseProperty { get; set; }
[JsonProperty("fOO")]
public string Foo { get; set; }
[JsonProperty("BAR")]
public string Bar { get; set; }
}
}
The reason you are seeing this is that CamelCasePropertyNamesContractResolver is intentionally designed to override the casing of dictionary keys and explicitly set property names, as can be see from the reference source:
public CamelCasePropertyNamesContractResolver()
{
NamingStrategy = new CamelCaseNamingStrategy
{
ProcessDictionaryKeys = true,
OverrideSpecifiedNames = true
};
}
If you don't want that, you have several options to prevent casing of explicit names without creating your own custom contract resolver type.
Firstly, you could serialize using a DefaultContractResolver with NamingStrategy = new CamelCaseNamingStrategy():
var settings = new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver { NamingStrategy = new CamelCaseNamingStrategy() }
};
var output2 = JsonConvert.SerializeObject(foo, settings);
This leaves CamelCaseNamingStrategy.OverrideSpecifiedNames at its default value of false.
Secondly, if you don't have access to the contract resolver of your framework, you could set JsonPropertyAttribute.NamingStrategyType = typeof(DefaultNamingStrategy) on specific properties, like so:
public class FooBar
{
public string CamelCaseProperty { get; set; }
[JsonProperty("fOO")]
public string Foo { get; set; }
[JsonProperty("BAR", NamingStrategyType = typeof(DefaultNamingStrategy))]
public string Bar { get; set; }
}
Thirdly, if you want your entire object to ignore the naming strategy of the current contract resolver, you can apply [JsonObject(NamingStrategyType = typeof(TNamingStrategy))] to your object:
[JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))]
public class FooBar
{
public string CamelCaseProperty { get; set; }
[JsonProperty("fOO")]
public string Foo { get; set; }
[JsonProperty("BAR")]
public string Bar { get; set; }
}
Notes:
While it is also possible to modify the NamingStrategy of an instance of CamelCasePropertyNamesContractResolver, since the latter shares contract information globally across all instances of each type, this can lead to unexpected side-effects if your application tries to use multiple instances of CamelCasePropertyNamesContractResolver. No such problem exists with DefaultContractResolver, so it is safer to use when any customization of casing logic is required.
When using or subclassing DefaultContractResolver, you may want to cache the contract resolver for best performance, since it does not share contract information globally across all instances of each type.
I don't know why Json.NET's camel case resolver is designed to override specified names, it may be for historical reasons.
Naming strategies were first introduced in Json.NET 9.0.1 so this answer works only for that version and later.
JsonProperty Attribute is not honoured when you use a ContractResolver.
What you can do to solve this issue is override the ContractResolver:
public class MyResolver : CamelCasePropertyNamesContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
if(member.GetCustomAttribute<JsonPropertyAttribute>() is JsonPropertyAttribute jsonProperty)
{
property.PropertyName = jsonProperty.PropertyName;
}
return property;
}
}
And use your Resolver:
var output2 = JsonConvert.SerializeObject(foo, new JsonSerializerSettings { ContractResolver = new MyResolver() });
I'm running into a problem where an ASP.NET MVC html helper method I created is not being "regenerated" each time it is called.
The purpose of the helper method is to create Javascript objects to be used in an angularjs framework. For example, here's a code snippet where the helper method is used (called from within a script tag of an html page):
var app = angular.module( "appName", ["ui.bootstrap"] );
app.controller( 'appCtrl', function( $scope ) {
$scope.model = #Html.ToJavascript( Model, new string[] { "FirstName", "LastName", "ID", "Role" } );
} );
Model is an instance of a class which has a variety of properties, but I only want FirstName, LastName, ID and Role to get serialized to a javascript object.
The ToJavascript() helper method is defined in a statis class as follows:
public static HtmlString ToJavascript( this HtmlHelper helper, object toConvert, string[] includedFields = null, Formatting formatting = Formatting.Indented, ReferenceLoopHandling loopHandling = ReferenceLoopHandling.Ignore )
{
using( var stringWriter = new StringWriter() )
using( var jsonWriter = new JsonTextWriter( stringWriter ) )
{
var serializer = new JsonSerializer()
{
// Let's use camelCasing as is common practice in JavaScript
ContractResolver = new SpecificFieldsResolver( includedFields ),
Formatting = formatting,
ReferenceLoopHandling = loopHandling,
};
// We don't want quotes around object names
jsonWriter.QuoteName = false;
serializer.Serialize( jsonWriter, toConvert );
return new HtmlString( stringWriter.ToString() );
}
}
This utilizes Json.NET to do the actual serialization.
One of the many cool features of Json.NET is that it lets you define, on the fly, which fields get serialized. That's what the SpecificFieldsResolver does. I've defined it as follows:
public class SpecificFieldsResolver : CamelCasePropertyNamesContractResolver
{
private string[] _included;
public SpecificFieldsResolver( string[] included )
{
_included = included;
}
protected override JsonProperty CreateProperty( MemberInfo member, MemberSerialization memberSerialization )
{
JsonProperty prop = base.CreateProperty( member, memberSerialization );
bool inclField = ( _included == null )
|| _included.Contains( member.Name, StringComparer.CurrentCultureIgnoreCase );
prop.ShouldSerialize = obj => inclField;
return prop;
}
}
What's confusing me is the way that CreateProperty() gets called. Specifically, it seems to only get called once for each type of object being serialized.
That's a problem because in another cshtml file I have another call to ToJavascript() which is attempting to serialize the same type of object, but with different fields to be output from the serialization:
var app = angular.module( "app2Name", ["ui.bootstrap"] );
app.controller( 'app2Ctrl', function( $scope ) {
$scope.model = #Html.ToJavascript( Model, new string[] { "FirstName", "LastName", "ID", "Role", "Category", "VoterID" } );
} );
Category and VoterID are also valid class fields. But ToJavascript() doesn't seralize them. Instead, it only serializes the fields defined in the first call to ToJavascript()...even though that call takes place in a different cshtml file. It's as if SpecificFieldsResolver remembers the JsonProperty objects it creates.
Thoughts?
Update
Thanx to dbc for diagnosing exactly what was wrong and suggesting a workaround. I adapated it slightly because I rely on Json.NET's camel case name resolution in several resolvers:
public class CamelCaseNameMapper : CamelCasePropertyNamesContractResolver
{
public string ToCamelCase( string propertyName )
{
return ResolvePropertyName( propertyName );
}
}
public class MaoDefaultContractResolver : DefaultContractResolver
{
private CamelCaseNameMapper _mapper = new CamelCaseNameMapper();
protected override string ResolvePropertyName( string propertyName )
{
return _mapper.ToCamelCase( propertyName );
}
}
Now every resolver, such as my SpecificFieldsResolver, which derives from MaoDefaultContractResolver automatically inherits camel casing but avoids the caching problem the dbc identified.
This appears to be a bug with CamelCasePropertyNamesContractResolver. Its base class, DefaultContractResolver, has two constructors: a parameterless constructor, and a DefaultContractResolver (Boolean) version (just made obsolete in Json.NET 7.0). This parameter has the following meaning:
shareCache
Type: System.Boolean
If set to true the DefaultContractResolverwill use a cached shared with other resolvers of the same type. Sharing the cache will significantly improve performance with multiple resolver instances because expensive reflection will only happen once. This setting can cause unexpected behavior if different instances of the resolver are suppose to produce different results. When set to false it is highly recommended to reuse DefaultContractResolver instances with the JsonSerializer.
The default is false.
Unfortunately, the default constructor for CamelCasePropertyNamesContractResolver sets the value to true:
public class CamelCasePropertyNamesContractResolver : DefaultContractResolver
{
public CamelCasePropertyNamesContractResolver()
#pragma warning disable 612,618
: base(true)
#pragma warning restore 612,618
{
NamingStrategy = new CamelCaseNamingStrategy
{
ProcessDictionaryKeys = true,
OverrideSpecifiedNames = true
};
}
}
Further, there is no second constructor with the shareCache option. This breaks your SpecificFieldsResolver.
As a workaround, you could derive your resolver from DefaultContractResolver and use CamelCaseNamingStrategy to do the name mapping:
public class IndependentCamelCasePropertyNamesContractResolver : DefaultContractResolver
{
public IndependentCamelCasePropertyNamesContractResolver()
: base()
{
NamingStrategy = new CamelCaseNamingStrategy
{
ProcessDictionaryKeys = true,
OverrideSpecifiedNames = true
};
}
}
public class SpecificFieldsResolver : IndependentCamelCasePropertyNamesContractResolver
{
// Remainder unchanged
}
Note that if you are using a version of Json.NET prior to 9.0, CamelCaseNamingStrategy does not exist. Instead a kludge nested CamelCasePropertyNamesContractResolver can be used to map the names:
public class IndependentCamelCasePropertyNamesContractResolver : DefaultContractResolver
{
class CamelCaseNameMapper : CamelCasePropertyNamesContractResolver
{
// Purely to make the protected method public.
public string ToCamelCase(string propertyName)
{
return ResolvePropertyName(propertyName);
}
}
readonly CamelCaseNameMapper nameMapper = new CamelCaseNameMapper();
protected override string ResolvePropertyName(string propertyName)
{
return nameMapper.ToCamelCase(propertyName);
}
}
I am using http://json.codeplex.com/ and I am talking to a Ruby based Rest API. Problem is that most of the properties have a ruby underscore naming convention. I am wondering if anyone knows of a way so that I can avoid having to Add lots of JsonProperty.
For example I want to avoid adding the JsonProperty attribute and have a convention built into the serializer settings so that it knows to try and map properties with an underscore in the to the .NET naming convention :)
public class Member
{
[JsonProperty(PropertyName = "avatar_url")]
public string AvatarUrl { get; set; }
[JsonProperty(PropertyName = "twitter_screen_name")]
public string TwitterScreenName { get; set; }
[JsonProperty(PropertyName = "website_url")]
public string WebSiteUrl { get; set; }
}
Update - September 2016:
Json.NET 9.0.1 has SnakeCaseNamingStrategy. You can use that to have twitter_screen_name style properties automatically.
Inherit from DefaultContractResolver and override ResolvePropertyName to format property names as you'd like.
CamelCasePropertyNamesContractResolver does a similar global change to property names.
Read this : http://nyqui.st/json-net-newtonsoft-json-lowercase-keys
public class UnderscoreMappingResolver : DefaultContractResolver
{
protected override string ResolvePropertyName(string propertyName)
{
return System.Text.RegularExpressions.Regex.Replace(
propertyName, #"([A-Z])([A-Z][a-z])|([a-z0-9])([A-Z])", "$1$3_$2$4").ToLower();
}
}
As of version 9, a new naming strategy property exists to do this, and it has a built-in SnakeCaseNamingStrategy class. Use the code below and register contractResolver as SerializerSettings.ContractResolver.
var contractResolver = new DefaultContractResolver();
contractResolver.NamingStrategy = new SnakeCaseNamingStrategy();
That class does not include dictionaries by default, and it does not override any manually-set property values. Those are the two parameters that can be passed in the overload:
// true parameter forces handling of dictionaries
// false prevents the serializer from changing anything manually set by an attribute
contractResolver.NamingStrategy = new SnakeCaseNamingStrategy(true, false);
This one worked for me
var settings = new JsonSerializerSettings
{
ContractResolver = new PascalCaseToUnderscoreContractResolver()
};
var rawJson = "{ test_property:'test' }"
var myObject = JsonConvert.DeserializeObject<MyObjectType>(rawJson, settings);
Using Humanizer function "Underscore"
https://www.nuget.org/packages/Humanizer/1.37.7
http://humanizr.net/#underscore
public class PascalCaseToUnderscoreContractResolver : DefaultContractResolver
{
protected override string ResolvePropertyName(string propertyName) => propertyName.Underscore();
}
MyObjectType class
public Class MyObjectType
{
public string TestProperty {get;set;}
}