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.
Related
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; }
My question is simple, but a little more specific than other questions related to serializing enumerated types as strings.
Consider the following piece of code:
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
public enum MyEnum
{
TypeOne,
TypeTwo,
TypeThree
}
public class Foo
{
[JsonConverter(typeof(StringEnumConverter))]
public MyEnum Types { get; set; }
}
When the Web API controller sends serialized Foo objects, they may look something like this:
{
"Type" : "TypeTwo"
}
My Question: is it possible to send serialized enums as strings with spaces before each capital letter? Such a solution would produce JSON like this:
{
"Type" : "Type Two"
}
Let me know if there's any additional information needed to solve my problem. Thanks!
EDIT:
It's preferable if the enums are only converted to strings with spaces while serializing them to JSON. I'd like to exclude spaces while using MyEnum.ToString() on the backend.
Try adding EnumMember as shown below,
[JsonConverter(typeof(StringEnumConverter))]
public enum MyEnum
{
[EnumMember(Value = "Type One")]
TypeOne,
[EnumMember(Value = "Type Two")]
TypeTwo,
[EnumMember(Value = "Type Three")]
TypeThree
}
You may need to install a package called System.Runtime.Serialization.Primitives from Microsoft to use that.
fromI have this linq query to build a json (of string and bool?) from an IQueryable :
var ret = from c in results select new { country = c.EN, schengen = c.Schengen };
I would like to append new items to it manually : (pseudo code)
ret = ret /* add this -> */ { country = "some new country", schengen = true }
I've tried to do that :
//End up with cannot convert from AnonymousType#2 to AnonymousType#1
var listRet = ret.ToList();
listRet.Add(new { country ="", schengen = true });
As it is an anonymous type that I build I cannot find a way to add it. I always end up with conversion error
Anonymous types are still statically typed and C# compiler generates a separate class for each anonymous type definition occurrence. You need a named class in this case. It's a good practice for data transfer objects anyway. I use JSON DTOs like this, using data annotations and DataContractJsonSerializer.
[DataContract]
public class CountryInfo
{
[DataMember(Name = "country")]
public string Country { get; set; }
[DataMember(Name = "schengen", EmitDefaultValue = false)
public bool? Schengen { get; set; }
}
This way I have a well-documented JSON "protocol", which also uses C# naming conventions before serialization, but JS naming conventions after.
Well it's obvious. Generics are invariant. So another type won't fit. You can use List<object> or a named class to store the values.
If you want to change the result manually, you need to cast it into a static type instead of a dynamic one. Create a class that has a string country and bool schengen(You can even create the class within your current class to hide it from the rest of the application, if that's applicable), cast your linq results into that and then just add to it.
Let's say I have:
public class Item
{
public string SKU {get; set; }
public string Description {get; set; }
}
....
Is there a built-in method in .NET that will let me get the properties and values for variable i of type Item that might look like this:
{SKU: "123-4556", Description: "Millennial Radio Classic"}
I know that .ToString() can be overloaded to provide this functionaility, but I couldn't remember if this was already provided in .NET.
The format you've described as an example looks a lot like JSON, so you could use the JavaScriptSerializer:
string value = new JavaScriptSerializer().Serialize(myItem);
If it is not JSON you are using and just normal C# class, have alook at System.Reflection namespace
something similar would work
Item item = new Item();
foreach (PropertyInfo info in item.GetType().GetProperties())
{
if (info.CanRead)
{
// To retrieve value
object o = info.GetValue(myObject, null);
// To Set Value
info.SetValue("SKU", "NewValue", null);
}
}
Provided with, you should have proper get and set in place for the properties you want to work over.
The XMLSerializer will work as well: http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.aspx
You could use a JSON library such as JSON.NET or the built-in JavaScriptSerializer.
If I have an enum like this
public enum Hungry
{
Somewhat,
Very,
CouldEatMySocks
}
and a custom attribute like this
public class HungerAttribute : Attribute
{
public Hungry HungerLevel { get; set; }
public Hungry? NullableHungerLevel { get; set; }
}
I can do this
[Hunger(HungerLevel = Hungry.CouldEatMySocks)]
public class Thing1
but I can't do this
[Hunger(NullableHungerLevel = Hungry.CouldEatMySocks)]
public class Thing2
It generates an error that says "'NullableHungerLevel' is not a valid named attribute argument because it is not a valid attribute parameter type".
Why is that not allowed? I understand that fundamentally it just isn't on the list of accepted types. The valid types seem to be primitives, enums, string, type, and one dimensional arrays of the preceding types.
Is this just an old rule that did not get updated when Nullable came along?
Hungry? is equal to Nullable<Hungry>, which in terms mean that
[Hunger(NullableHungerLevel = Hungry.CouldEatMySocks)]
is equal to
[Hunger(NullableHungerLevel = new Nullable<Hungry>(Hungry.CouldEatMySocks))]
Since you can only use constant values in named attribute arguments you will have to resort to Shimmy's solution.
To get around this create another initializer in your Attribute:
class Program
{
[Hunger()]
static void Main(string[] args)
{
}
public sealed class HungerAttribute : Attribute
{
public Hungry? HungerLevel { get; }
public bool IsNull => !_HungerLevel.HasValue;
public HungerAttribute()
{
}
//Or:
public HungerAttribute(Hungry level)
{
HungerLevel = level;
}
}
public enum Hungry { Somewhat, Very, CouldEatMySocks }
}
I understand that you're not going to use both properties.
Attributes may have as only parameters primitives, typeof expressions and array-creation expression.
Nullable is a struct.
Therefore it is not allowed there.
I suspect the assembly file format itself doesn't allow storage of complex types like structs in the place where attribute values are stored.
I don't know of any plans to change that. But I cannot explain why this restriction exist.
Instead of creating nullable enum, you can create default value for that enum. Enum pick default from 1st value, so set your enum like this
public enum Hungry
{
None,
Somewhat,
Very,
CouldEatMySocks
}
in your code you could do this to check for null
if(default(Hungry) == HungerLevel)//no value has been set