Serialize enum values as camel cased strings using StringEnumConverter - c#

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

Related

c# Newtonsoft.json custom Enum serialization

I need to customize the way Newtonsoft.Json serializes an object, in particular about Enum types.
Given a sample class like this:
public class TestEnumClass
{
public Enum ReferencedEnum { get; set; }
public string OtherProperty { get; set; }
public StringSplitOptions OtherEnum { get; set; }
}
The default serialization will happen this way:
var testEnumClass = new TestEnumClass
{
ReferencedEnum = StringComparison.OrdinalIgnoreCase,
OtherProperty = "Something",
OtherEnum = StringSplitOptions.None
};
var serialized = JsonConvert.SerializeObject(testEnumClass, Formatting.Indented);
And the serialized string will be:
{
"ReferencedEnum": 5,
"OtherProperty": "Something",
"OtherEnum": 0
}
Here I have 2 problems:
I cannot guarantee that the order of the Enums will remain the same (here I am using Enums included in the framework, but my project has other Enums that derive from ": Enum"), so I cannot keep the number as the serialized value of the Enum.
Secondly, and more important, is the fact that the "ReferencedEnum" field is declared as "Enum", and any kind of Enum can be written in that field (ReferencedEnum = AnyEnum.AnyEnumValue).
This leads to the fact that when deserializing the value, I need to know the original Enum type (in the example is StringComparison).
I was thinking of using a Converter (deriving from ": JsonConverter") and manipulating what is written and read. The result I was thinking was something like this:
{
"ReferencedEnum": {
"EnumType": "StringComparison",
"EnumValue": "OrdinalIgnoreCase"
},
"OtherProperty": "Something",
"OtherEnum": "StringSplitOptions.None"
}
I this way the deserializer would know:
for "Enum" properties, the original type and the string value.
for "typed Enum" (specific enum) properties, the full type and value.
What I cannot absolutely add is a reference to the converter in the model class like this:
[JsonConverter(typeof(EnumConverter))]
public Enum ReferencedEnum { get; set; }
And I also would avoid to have the "$type" field in the serialized string (except if this is the only solution).
By the way, I can add a generic attribute like this:
[IsEnum]
public Enum ReferencedEnum { get; set; }
Does somebody have any idea of how can I get the result needed?
Thank you!
I've been in the very same issue and developed a nuget package named StringTypeEnumConverter, that solves it.
You can check the project here.
The usage will be as simple as any other converter.
This converter derives from an already existing "StringEnumConverter", that writes the string value of the enum, instead of its numeric counterpart.
I added the support to writing the type name too.
The result will be like: "StringSplitOptions.None" (this is a string value, not an object).
Note that this converter should be applied for both writing and reading, as the resulting json would not be compatible with other readers not including this converter.
You should consider using this package only if you cannot avoid using enums in your model.
I would also suggest you to spend time to check if (custom) enums could be transformed to classes.
J.

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.

Snake Cased names for enum values

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.

Use JSON.net Binder/TypeNameHandling for nested types

Using JSON.net, I want to deserialize a list of types that inherit from an abstract class. I have used KnownTypesBinder (source) like
var jsonSettings = GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings;
jsonSettings.TypeNameHandling = TypeNameHandling.Auto;
jsonSettings.Binder = new KnownTypesBinder { KnownTypes = new List<Type> { ... } };
Now this works perfectly fine in the WEB API modelbinder; KnownTypesBinder.BindToType is being called an the object can be deserialized. In a different part of the web application I have to deserialize a JSON string, so I would like to re-use the same JsonFormatter. According to the docs, the following should work;
JsonConvert.DeserializeObject<T>(String, JsonSerializerSettings);
However when I do just that:
JsonConvert.DeserializeObject<A>(jsonString, GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings);
The following error is thrown:
JsonSerializationException. Could not create an instance of type C. Type is an interface or abstract class and cannot be instantiated.
My types look something like this;
class A {
public B B { get; set; }
}
class B {
public List<C> C { get; set; }
}
abstract class C { }
class C1: C { }
class C2: C { }
Also creating a new JsonSerializerSettings and setting Binder and TypeNameHandling makes no difference. I found that KnownTypesBinder.BindToType is not being called at all. Is there something in JSON.net that I'm missing?
Feeling stupid here. The discriminator used by JSON.net is called "$type". As I was using curl to send the POST payload, bash tried to resolve $type to an environment variable. As there was no such variable, the final JSON was simply {"": "C"} instead of {"$type": "C"}.

JSON.NET: Define C# field that can hold two different kind of objects

I have the following objects that I serialize with JSON.NET (not this in particular, but with similar structure).
A basic class for request
public class Request
{
public string version = "1.0";
public RequestParams params;
public Request(RequestParams params)
{
this.params = params;
}
}
A basic payload class
public abstract class RequestParams
{ }
Payload classes
public SampleRequest : RequestParams
{
public string someInfo = "param info";
}
Usage
new Request(new SampleRequest());
This is all fine when I know the structure of the requests. However, sometimes I need to define dynamic object as "params" parameter. That is, I need to have "params" object be treated as a Dictionary (but without the []), so that I receive JSON in the format:
{
"version":"1.0",
"params":{
"dynamic":"x",
...any number of dynamically added fields
"dynamic2":"y"
}
}
How can I do it?
Create a base Request class that pulls in its information from a params dictionary. This is your dynamic request object.
Create classes that inherit from Request that operate upon the dictionary, instead of members. These are you 'known' requests.
params is a keyword in .Net. Consider using another varaible name.
You can use dynamic and ExpandoObject:
dynamic parameters = new ExpandoObject();
parameters.Name = "Name";
parameters.Age = 30;
// Adding as much property as you like dynamically
this after serialization becomes:
{
'Name': 'Name',
'Age': 32,
/* any other dynamic property */
}

Categories

Resources