Snake Cased names for enum values - c#

Is there any built in functionality in Newtonsoft.Json for serializing enum values into their snake-cased names?
currently, I am providing values manually:
[JsonConverter(typeof(StringEnumConverter))]
enum MyEnum {
[EnumMember(Value = "value_one")]
ValueOne,
}

Optional snake-casing of enum values has been implemented in Json.NET 12.0.1. It is now possible to specify a NamingStrategy for StringEnumConverter:
New feature - Added support for NamingStrategy to StringEnumConverter
Thus you can pass SnakeCaseNamingStrategy into any of several of the constructors for StringEnumConverter, e.g. new StringEnumConverter(typeof(SnakeCaseNamingStrategy)).
Using this, you can now specify that enums should be snake-cased globally when serialized by adding an appropriate converter to JsonSerializerSettings.Converters:
var settings = new JsonSerializerSettings
{
Converters = { new StringEnumConverter(typeof(SnakeCaseNamingStrategy)) },
};
var json = JsonConvert.SerializeObject(MyEnum.ValueOne, settings);
Assert.IsTrue(json == "\"value_one\""); // Passes successfully
Or, alternatively, SnakeCaseNamingStrategy can be applied to select enums as follows:
[JsonConverter(typeof(StringEnumConverter), typeof(SnakeCaseNamingStrategy))]
enum MyEnum
{
ValueOne,
// Other values...
}
For further information see Issue #1862: [Feature] StringEnumConverter takes a NamingStrategy argument.

Related

How to Override a Default JsonConverter (specified in an attribute)

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.

Custom Json.NET converter should not serialize a property

I have written a Json.NET converter which outputs all the set flags as an array.
enum SampleEnum
{
None = 0,
ValueA = 2,
ValueB = 4
}
SampleEnum flags = SampleEnum.ValueA | SampleEnum.ValueB;
// JSON: ["ValueA", "ValueB"]
Now in case flags is SampleEnum.None, the property should not be serialized. Therefore I just don't write anything to the JsonWriter. Here is the code of the WriteJson method of the converter.
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value is SampleEnum enumValue)
{
IEnumerable<SampleEnum> setFlags = GetSetFlags<SampleEnum>(enumValue);
IEnumerable<string> flagNames = setFlags
.Where(flag => flag != SampleEnum.None) // Filter out 'None'
.Select(flag => flag.ToString());
if (flagNames.Any())
{
JArray jArray = JArray.FromObject(flagNames, serializer);
jArray.WriteTo(writer);
}
// Else omit this property
}
}
However, if I have a property of type SampleEnum in my class and its value is SampleEnum.None, the property is serialized and the JSON value is null.
class SerializedClass
{
[JsonConverter(typeof(ArrayEnumConverter))]
public SampleEnum EnumValue { get; set; }
}
SerializedClass obj = new SerializedClass
{
EnumValue = SampleEnum.None
};
string json = JsonConvert.SerializeObject(obj, new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore
});
The output is the following:
{
"EnumValue": null
}
The output I wish to see:
{}
What can I do so that the property is omitted instead of being null?
P.S.: I've read about Conditional Property Serialization, but the ShouldSerialize methods are not suitable in my case and I haven't figured out yet how to use IContractResolver for my case.
A custom JsonConverter cannot prevent its value from being serialized, because the property name referring to it will already have been written out by the time the converter is invoked. In Json.NET's architecture it is the responsibility of the containing type to decide which of its properties to serialize; the value converter then decides how to serialize the value being written.
As an alternative, the setting DefaultValueHandling.Ignore can be used to skip serialization of enum members even when a converter is applied. Since SampleEnum.None has the value 0, it is the default value for your flags enumeration. Members with this value will this be skipped when the setting is enabled whether or not a converter is applied.
You can enable DefaultValueHandling by applying it via JsonPropertyAttribute.DefaultValueHandling:
public class SerializedClass
{
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
[JsonConverter(typeof(ArrayEnumConverter))]
public SampleEnum SampleEnum { get; set; }
}
Sample fiddle here.
Incidentally, you should consider marking your SampleEnum with the [Flags] attribute:
[Flags]
public enum SampleEnum
{
None = 0,
ValueA = 2,
ValueB = 4
}
This is the recommended best practice for flag enums:
Designing Flag Enums
√ DO apply the System.FlagsAttribute to flag enums. Do not apply this attribute to simple enums.

Converting enum values to arbitrary integers when using Newtonsoft.Json

I have an enum:
public enum IdleDelayBreakMode
{
Repeat,
ShowNext
}
I'm using NewtonSoft.Json to convert to json objects containing properties of that enum's type. What's the best solution to serialize values of that enum to arbitrary integers? Ideally i would like to do something like on the snippet below and i want to know if maybe there's a built-in solution for that:
public enum IdleDelayBreakMode
{
[JsonValue(100)]
Repeat, // When serializing will be converted to 100
[JsonValue(200)]
ShowNext // When serializing will be converted to 200
}
You can just set enum values like that:
public enum IdleDelayBreakMode
{
Repeat = 100,
ShowNext = 200
}
Newtonsoft.Json will use enum values. There is no need to set attributes. This way it will be consistent across the system whether you need to serialize it to JSON, save it in the database using Entity Framework etc.
Are you using your enum as integer constants storage?
In this case you might want to inherit it from int:
public enum IdleDelayBreakMode : int
Assuming you don't want to modify the underlying enum values (as is shown in the other answers), you can decorate your enum vales with [EnumMember(Value = "Name")] attributes and use the alternate numeric values as the name strings:
[JsonConverter(typeof(StringEnumConverter))]
[DataContract]
public enum IdleDelayBreakMode
{
[EnumMember(Value = "100")]
Repeat,
[EnumMember(Value = "200")]
ShowNext
}
You will also need to serialize using StringEnumConverter, either by adding [JsonConverter(typeof(StringEnumConverter))] directly to the enum or by applying it globally according to this answer.
This works with [Flags] as well. Serializing both values of the following:
[Flags]
[DataContract]
[JsonConverter(typeof(StringEnumConverter))]
public enum IdleDelayBreakModeFlags
{
[EnumMember(Value = "100")]
Repeat = (1 << 0),
[EnumMember(Value = "200")]
ShowNext = (1 << 1),
}
produces "100, 200".
Adding these attributes will cause DataContractSerializer as well as Json.NET to use these alternate name strings. If you would prefer not to affect the behavior of the data contract serializer, remove [DataContract] but keep the [EnumMember] attributes.
Just set the integer values in your enum items directly instead of using an attribute:
public enum IdleDelayBreakMode
{
Repeat = 100,
ShowNext = 200
}
JSON.Net will them use the integer values when serializing/deserializing a property of type IdleDelayBreakMode

Serialize enum values as camel cased strings using StringEnumConverter

I'm trying to serialize a list of objects to JSON using Newtonsoft's JsonConvert. My Marker class includes an enum, and I'm trying to serialize it into a camelCase string. Based on other Stackoverflow questions, I'm trying to use the StringEnumConverter:
public enum MarkerType
{
None = 0,
Bookmark = 1,
Highlight = 2
}
public class Marker
{
[JsonConverter(typeof(StringEnumConverter)]
public MarkerType MarkerType { get; set; }
}
This partially works, but my MarkerType string is PascalCase when I call:
var json = JsonConvert.SerializeObject(markers, Formatting.None);
Result:
{
...,
"MarkerType":"Bookmark"
}
What I'm really looking for is:
{
...,
"MarkerType":"bookmark"
}
The StringEnumConverter docs mention a CamelCaseText property, but I'm not sure how to pass that using the JsonConverterAttribute. The following code fails:
[JsonConverter(typeof(StringEnumConverter), new object[] { "camelCaseText" }]
How do I specify the CamelCaseText property for the StringEnumConverter in a JsonConverterAttribute?
JsonConverterAttribute has two constructors, one of which takes a parameter list (Object[]). This maps to the constructor of the type from the first parameter.
StringEnumConverter can handle this with most of its non-default constructors.
The first one is obsolete in JSON.net 12+
The second one allows you to specify a NamingStrategy Type; the CamelCaseNamingStrategy does the trick. Actually, this is true for three out of the six constructors provided.
Note: one other constructor breaks the mold, asking for an instance of a NamingStrategy instead of a type.
It would look like this:
[JsonConverter(typeof(StringEnumConverter), typeof(CamelCaseNamingStrategy))]
public MarkerType MarkerType { get; set; }

Keep casing when serializing dictionaries

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

Categories

Resources