I have an enum:
public enum FilterOperator
{
[EnumMember(Value = "eq")]
Equals,
[EnumMember(Value = "gt")]
GreaterThan,
[EnumMember(Value = "lt")]
LessThan,
[EnumMember(Value = "in")]
In,
[EnumMember(Value = "like")]
Like
}
and a class that includes an enum property:
public class GridFilter
{
[JsonProperty("operator")]
[JsonConverter(typeof(StringEnumConverter))]
public FilterOperator Operator { get; set; }
}
The object is passed in via a WebAPI action and deserializes as expected for "like" and "in" but it doesn't for "lg" or "gt". Any idea why?
UPDATE: Well the reason "like" and "in" work is that they match the enum name. Renaming GreaterThan to Gt (etc) works. So the real issue is why isn't StringEnumConverter being used?
Well, you must place the [JsonConverter(typeof(StringEnumConverter))] attribute directly on the enum declaration instead of the Operator property of GridFilter if you want it to be used when deserializing outside the context of the class GridFilter:
[JsonConverter(typeof(StringEnumConverter))] // Add this
public enum FilterOperator
{
[EnumMember(Value = "eq")]
Equals,
[EnumMember(Value = "gt")]
GreaterThan,
[EnumMember(Value = "lt")]
LessThan,
[EnumMember(Value = "in")]
In,
[EnumMember(Value = "like")]
Like
}
public class GridFilter
{
[JsonProperty("operator")]
//[JsonConverter(typeof(StringEnumConverter")] // Remove this
public FilterOperator Operator { get; set; }
}
Thanks for everyone's help! I realized what I've done. Sadly it's pretty dumb, I apologize in advanced for the run around.
Since I am using GET I am sending the parameters as url query parameters so WebAPI is using the normal ModelBinder to map names and not JSON.NET. I'm not actually sending JSON so this make total sense. This question helped me realize this:
Complex type is getting null in a ApiController parameter
My choices are create a custom model binder that handles the enum correctly or change to a POST and send the data with JSON.stringify().
This is just a guess, and I haven't tested it.
I looked at the documentation for EnumMemberAttribute, and it says:
To use EnumMemberAttribute, create an enumeration and apply the DataContractAttribute attribute to the enumeration. Then apply the EnumMemberAttribute attribute to each member that needs to be in the serialization stream.
That's for the DataContractSerializer, of course, but I'm thinking perhaps JSON.net takes that same rule into account?
I'd try applying [DataContract] to the enum.
[DataContract]
public enum FilterOperator
{
[EnumMember(Value = "eq")]
Equals,
[EnumMember(Value = "gt")]
GreaterThan,
[EnumMember(Value = "lt")]
LessThan,
[EnumMember(Value = "in")]
In,
[EnumMember(Value = "like")]
Like
}
It seems arbitrary, and redundant. And I know JSON.net doesn't typically depend on that sort of thing, but maybe in this case it does?
I'm also noticing that it appears the DataContractSerializer ignores elements without [EnumMember] if [DataContract] is present, so it might have to be this way for backwards compatibility. Again, not super logical. But that's all I've got.
Edit: In true developer fashion, rather than just testing this, I went into the source code. The part that reads the EnumMemberAttribute can be found here on line 55, and it does this:
n2 = f.GetCustomAttributes(typeof(EnumMemberAttribute), true)
.Cast<EnumMemberAttribute>()
.Select(a => a.Value)
.SingleOrDefault() ?? f.Name;
That makes me think that what you've got should be working.
Edit 2:
Alright, so this is odd. I just tried it myself and found it working.
public enum FilterOperator
{
[EnumMember(Value = "eq")]
Equals,
[EnumMember(Value = "gt")]
GreaterThan,
[EnumMember(Value = "lt")]
LessThan,
[EnumMember(Value = "in")]
In,
[EnumMember(Value = "like")]
Like
}
public class GridFilter
{
[JsonProperty("operator")]
[JsonConverter(typeof(StringEnumConverter))]
public FilterOperator Operator { get; set; }
}
[TestMethod]
public void enumTest()
{
GridFilter gf = new GridFilter()
{
Operator = FilterOperator.GreaterThan
};
var json = JsonConvert.SerializeObject(gf);
// json yields {"operator":"gt"}
var ret = JsonConvert.DeserializeObject<GridFilter>(json);
// ret.Operator yields FilterOperator.GreaterThan
}
Related
How do I add multiple values in EnumMember attribute ?
[JsonProperty("type")]
public AssetType Type { get; set; }
[JsonConverter(typeof(EnumConverter))]
public enum AssetType
{
[EnumMember(Value = "node")]
Folder
}
I need something like
[JsonConverter(typeof(EnumConverter))]
public enum AssetType
{
[EnumMember(Value = "node","collection")]
Folder
}
Meaning the value that came from json if is node or collection I need to convert to folder AssetType
Either do as canton7 suggests and assign Folder to multiple members like so
enum AssetType
{
Folder,
[EnumMember(Value = "node")]
Node = Folder,
[EnumMember(Value = "collection")]
Collection = Folder
}
(and yes, AssertType.Node == AssertType.Folder)
Or if that is not possible in your case use your own JsonConverter to write the parsing method yourself.
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
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.
I am writing a wrapper to a REST API. So, whenever I am retrieving items from the server, I just need to play around with the parameters (in the uri). The way I am doing it now works fine but I just feel there must be another elegance way to do this maybe with enum or something.
I don't like that I need to 'know' what are the options are as the Dictionary type is string. I tried with Dictionary<EnumType, string>, but I have more than one type of enum. Plus, I am not sure how to map the EnumType (key) to the appropriate value.
Basically, I am trying to avoid as much as possible the use of magic keyword.
Here is my partial code:
public string GetUnreadItems()
{
var options = new Dictionary<string, string>();
options.Add("ItemType", "Unread");
options.Add("SortBy", "Latest");
// GetItemsBasedOn(options);
}
public string GetAllItems()
{
var options = new Dictionary<string, string>();
options.Add("ItemType", "All");
// GetItemsBasedOn(options);
}
public string GetItemsBasedOn(Dictionary<string, string> options)
{
// Do request here based on options passed
// and return the result to caller function
}
EDIT:
This is what I am trying to implement http://getpocket.com/developer/docs/v3/retrieve
I would like to implement the options sort, detailType, contentType, Favorite, and State. And each of the options have their own options but only one can be selected at a time.
There are a few ways you could go about improving your current design. I don't agree that the solution I am about to present to you is the ideal solution, but given that you have already identified that you would like to use an enum I think you will be content with this solution.
What you can do is define an enum marked with the [Flags] attribute. Assign to each flag in the enum a value that is a power of two. If you want to combine options to create a single option use bitwise or just like I did with the flag named "All" in the proceeding sample:
[Flags]
public enum GetItemOptions
{
Read = 0x1,
Unread = 0x2,
All = 0x1 | 0x2,
SortByOldest = 0x4,
SortByLatest = 0x8
}
From your code sample, the first call will now look like this:
GetItemsBasedOn(GetItemOptions.Unread | GetItemOptions.SortByLatest);
And the second will look like this:
GetItemsBasedOn(GetItemOptions.All);
In order to enable this design you will need to adjust your GetItemsBasedOn method signature so that it specifies an argument of the GetItemOptions enum type. Below is an example of how you can handle different settings.
public static void GetItemsBasedOn(GetItemOptions getItemOption)
{
if (getItemOption.HasFlag(GetItemOptions.SortByOldest) && getItemOption.HasFlag(GetItemOptions.SortByLatest))
throw new ArgumentException("I can't sort by both...");
if (getItemOption.HasFlag(GetItemOptions.Read))
{
Console.WriteLine("READ");
}
if (getItemOption.HasFlag(GetItemOptions.Unread))
{
Console.WriteLine("UNREAD");
}
if (getItemOption.HasFlag(GetItemOptions.SortByOldest))
{
Console.WriteLine("SORT BY OLDEST");
}
else if (getItemOption.HasFlag(GetItemOptions.SortByLatest))
{
Console.WriteLine("SORT BY NLATEST");
}
}
I don't think you know much about bit-wise operations, and for that reason I simplified the code sample as much as possible by utilizing the Enum.HasFlag method which simply checks if the given GetItemOptions enum has a flag specified.
You may have bared witness to this pattern before when using RegexOptions Enumeration or ControlStyles Enumeration
Update
I would suggest that you create an enum for each parameter and define a class like this:
public class PocketDataRequest
{
public State? State { get; set; }
public Favourite? Favourite { get; set; }
public ContentType? ContentType { get; set; }
public Sort? Sort { get; set; }
public DetailType? DetailType { get; set; }
public Dictionary<string, string> ToPostData()
{
return GetType().GetProperties()
.Where(p => p.GetValue(this, null) != null)
.ToDictionary(p => p.Name,
p => p.GetValue(this, null).ToString());
}
}
This would leverage the following syntax:
PocketDataRequest pocketDataRequest = new PocketDataRequest();
pocketDataRequest.State = State.Unread;
pocketDataRequest.Sort = Sort.Newest;
GetItemsBasedOn(pocketDataRequest.ToPostData());
In my implementations ToPostData method, I use LINQ and Reflection, that is just because I am lazy. You need to manually evaluate each enum value, especially if you want to change the enum names to something more appropriate. Also, my code will fail if you try and pass the parameter titled favorite. This is because favorite takes either the number "0" or "1". This is not a big problem because what you can do it define the enum like this:
public enum Favourite
{
UnfavouritedItems = 0,
FavouritedItems = 1
}
and then simply cast the value (Int32) and add that value to the Dictionary<string, string> or NameValueCollection.
I use .NET 3.5 for this.
I have an enum:
[System.SerializableAttribute()]
public enum MyEnum
{
[System.Xml.Serialization.XmlEnumAttribute("035")]
Item1,
Item2
}
I use this enum in a class:
[System.SerializableAttribute()]
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public MyEnum MyEnum { get; set; }
}
Now I want to create a new Eplomyee-instance, set the MyEnum-property by casting it from a string.
Then serialize it and save it in a file.
Employee bob = new Employee() {Id = 1, Name = "Bob"};
bob.MyEnum = (MijnEnum)Enum.Parse(typeof(MyEnum), string.Format(CultureInfo.InvariantCulture, "{0}", "035"));
XmlSerializer serializer = new XmlSerializer(typeof(Employee));
FileInfo fi = new FileInfo(#"C:\myfile.xml");
using (FileStream stream = fi.OpenWrite())
{
XmlWriterSettings xmlWriterSettings = new XmlWriterSettings { Encoding = Encoding.UTF8, OmitXmlDeclaration = true, Indent = true };
using (XmlWriter writer = XmlWriter.Create(stream, xmlWriterSettings))
{
serializer.Serialize(writer, bob); // this is place where it goes wrong
}
}
If I debug this, I see that the value of bob.MyEnum is 35
When I try to serialize, I get an exception:
There was an error generating the XML document.
Instance validation error: '35' is not a valid value for
WindowsFormsApplication.MyEnum.
What is going wrong and how can I solve this?
Let's start:
[System.SerializableAttribute()] // useless, valuetype is implicitly so
public enum MyEnum
{
[System.Xml.Serialization.XmlEnumAttribute("035")]
Item1,
Item2
}
Now the XmlEnumAttribute controls how that value is serialized and deserialized in XML.
IT HAS NOTHING TO WITH THE REST OF YOUR CODE! (sorry for the caps, but no-one else seems to get this).
So when a value of MyEnum.Item1 get serialized, "035" will be emitted.
Now the problem is how you want to assign this.
It is simple. Just assign like you would normally do. None these attributes change semantics of normal code, everything stays the same.
Example:
Employee bob = new Employee() {Id = 1, Name = "Bob", MyEnum = MyEnum.Item1};
There is abolutely no reason why Enum.Parse should even be considered here. The enum type and value is statically known.
If you did want to use Enum.Parse, use it like normal, example:
Enum.Parse(typeof(MyEnum), "Item1")
This is happening because, Enums are internally store as int. Hence your statement bob.MyEnum = (MijnEnum)Enum.Parse(typeof(MyEnum), string.Format(CultureInfo.InvariantCulture, "{0}", "035")); is running without issue. If you debug, value of bob.MyEnum is 35. When you deserialize this, deserializer searches for matching enum with int value 35, which is not there, as you are specifying Item1 and Item2. Hence you get an error.
This will work
bob.MyEnum = (MyEnum)Enum.Parse(typeof(MyEnum), "35");
public enum MyEnum {
Item1 = 35,
Item2
}
Ideally you should be doing this
bob.MyEnum = (MyEnum)Enum.Parse(typeof(MyEnum), "Single");
public enum MyEnum {
[System.Xml.Serialization.XmlEnumAttribute("Single")]
Item1,
Item2
}
Hope this helps you.
I changed the enum-parsing.
I used reflection to parse the string to the enum, as described in this article: http://www.codeguru.com/csharp/csharp/cs_syntax/enumerations/article.php/c5869
And now it works.