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.
Related
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
I have an XML document, and using deserialization, is there a way to combine two elements into one object?
XML example:
<Parameter1>3</Parameter1>
<Parameter2>4</Parameter2>
I want to create a list (of type Parameter) that contains both items, 3 and 4.
I've tried using XmlArrayItem such as:
[XmlArrayItem("Parameter1")]
[XmlArrayItem("Parameter2")]
[XmlArray]
public Parameter[] Parameters; // have also tried this as public List<Parameter> Parameters = new List<Parameter>();
I've tried using XmlElements (but I can't figure out how to combine them):
[XmlElement("Parameter1")]
public List<Parameter> Parameters = new List<Parameter>();
Is there any way to do this without just creating two separate lists and combining them at a later point?
Please note that changing the XML format is not an option.
Your XML has a schema that includes a choice element. A choice element indicates that one of a fixed set of elements -- <Parameter1> and <Parameter2> in your case -- will occur in the XML. XmlSerializer supports choice elements as is explained in Choice Element Binding Support:
If individual choice elements' types differ along with their names, Xsd.exe applies only XmlElementAttribute attributes to a public member. If they differ only by name, Xsd.exe applies an XmlChoiceIdentifierAttribute in addition, and adds extra logic for making the choice.
Thus, you have the following options to deserialize your XML:
Subclass your Parameter class and specify different types for each element name, using [XmlElementAttribute(String, Type)]. The specific Parameter subclass instantiated would thereby capture the XML element name.
I.e. you could do:
public abstract class Parameter
{
[XmlText]
public string Value { get; set; } // Could be int if you prefer.
}
public class Parameter1 : Parameter
{
}
public class Parameter2 : Parameter
{
}
[XmlType("Root")]
public class RootObject
{
[XmlElement("Parameter1", typeof(Parameter1))]
[XmlElement("Parameter2", typeof(Parameter2))]
public Parameter[] Parameters { get; set; }
}
If you want to use the same Parameter type to deserialize both <Parameter1> and <Parameter2> elements, you must introduce an ancillary XmlChoiceIdentifierAttribute array to capture the XML element name:
public class Parameter
{
[XmlText]
public string Value { get; set; }
}
[XmlType("Root")]
public class RootObject
{
[XmlElement("Parameter1", typeof(Parameter))]
[XmlElement("Parameter2", typeof(Parameter))]
[XmlChoiceIdentifier("ParametersElementName")]
public Parameter[] Parameters { get; set; }
[XmlIgnore]
public ParametersChoiceType[] ParametersElementName { get; set; }
}
[XmlType(IncludeInSchema = false)]
public enum ParametersChoiceType
{
Parameter1,
Parameter2,
}
After deserialization, the ParametersElementName array will have the same number of entries as the Parameters array, and the enum values therein will indicate the XML element name actually encountered for each parameter.
As a variation of option 2, if you do not need to capture the XML element name and just want to deserialize the values, you could create a "fake" choice array property as follows:
[XmlType("Root")]
public class RootObject
{
[XmlElement("Parameter1", typeof(Parameter))]
[XmlElement("Parameter2", typeof(Parameter))]
[XmlChoiceIdentifier("ParametersElementName")]
public Parameter[] Parameters { get; set; }
[XmlIgnore]
public ParametersChoiceType[] ParametersElementName
{
get
{
if (Parameters == null)
return null;
return Parameters.Select(p => ParametersChoiceType.Parameter1).ToArray();// Arbitrarily return ItemsChoiceType.Parameter1
}
set
{
// Do nothing - don't care.
}
}
}
XmlSerializer requires you to use one of these two options. If it cannot determine a correct element name by type or by item choice identifier, it will throw an InvalidOperationException with the message:
You need to add XmlChoiceIdentifierAttribute to the 'Parameters' member.
Prototype fiddle showing each option.
What if you do something like this:
//get the xml doc
const string str = #"<root>
<Parameter1>3</Parameter1>
<Parameter2>4</Parameter2>
</root>";
var xml = new XmlDocument();
//load it
xml.LoadXml(str);
//get the nodes where the names contain the string parameter
var xnList = xml.SelectNodes("//*[contains(name(),'Parameter')]");
//create a list of parameters
var list = new List<Parameter>();
//populate the list with the value in the node's innertext
foreach (XmlNode xn in xnList)
{
list.Add(new Parameter{ Value = int.Parse(xn.InnerText) } );
}
foreach(var param in list)
Console.WriteLine(param.Value); //should print 3 and 4
I am using this class as an example:
class Parameter{
public int Value { get; set; }
}
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
}
I have a class that looks like this (heavily simplified):
public class Foo
{
public enum Value
{
ValueOne,
ValueTwo
}
[XmlAttribute]
public Value Bar { get; set; }
}
I'm receiving an XML file from an external source. Their documentation states that the Foo element will only ever have "ValueOne" or "ValueTwo" in the Bar attribute (they don't supply an XSD).
So, I deserialize it like this:
var serializer = new XmlSerializer(typeof(Foo));
var xml = "<Foo Bar=\"ValueTwo\" />";
var reader = new StringReader(xml);
var foo = (Foo)serializer.Deserialize(reader);
... and that all works.
However, last night, they sent me some XML looking like this instead, and my deserialization failed (as it should):<Foo Bar="" />
Is there a good way to defensively code around this? Ideally I'd like to say something like "default to ValueOne, if something goes wrong here". I don't want to throw away the whole XML file, because a single attribute was mangled.
you can set you enum like this , so this will set "" value of enum to Unknown = 0 somewhat like default value
public enum Foo
{
[XmlEnum(Name = "")]
Unknown =0,
[XmlEnum(Name = "Single")]
One,
[XmlEnum(Name = "Double")]
Two
}
more detail check : XmlEnumAttribute Class
You can manually parse enum value by creating another property with the same XmlAttribute name:
public enum Value
{
// Default for unknown value
Unknown,
ValueOne,
ValueTwo
}
[XmlIgnore]
public Value Bar { get; set; }
[XmlAttribute("Bar")]
public string _Bar
{
get { return this.Bar.ToString(); }
set { this.Bar = Enum.TryParse(value, out Value enumValue) ? enumValue : Value.Unknown; }
}
Usage the same as your before
var serializer = new XmlSerializer(typeof(Foo));
var xml = "<Foo Bar=\"invalid value\" />";
var reader = new StringReader(xml);
var foo = (Foo)serializer.Deserialize(reader);
Shoot down XmlSerializer..
Use LINQ2XML for this simple task
XElement doc=XElement.Load("yourStreamXML");
List<Foo> yourList=doc.Descendants("Foo")
.Select(x=>
new Foo
{
Bar=(Enum.GetNames(typeof(Value)).Contains(x.Attribute("Bar").Value))?((this.Value)x.Attribute("Bar")):(this.Value.ValueOne);
}
).ToList<Foo>();
So,I am basically doing this
if(Enum.GetNames(typeof(Value)).Contains(x.Attribute("Bar").Value))
//if the xml bar value is a valid enum
Bar=(this.Value)x.Attribute("Bar");
else//xml bar value is not a valid enum..so use the default enum i.eValueOne
Bar=this.Value.ValueOne;
If I have the following:
public enum TYPE
{
One = 1,
Two = 2,
Three = 3
}
When I do:
var a = TYPE.One;
I would like it to populate the variable a with a string in the format "01". In other words two digits with a leading zero.
Is it possible to do this by assigning some method to the SomeEnum? I realized I could use TYPE.One.ToString("00")but I would like to have it self-contained in the enum and something very simple to use.
You can add a string to each enum element using the description attribute.
e.g.
public Enum MyEnum
{
[Description("Value A Description")]
ValueA,
[Description[("Value B Description")]
ValueB
}
To retrieve the description value, use an extender class
public static class MyEnumExtender
{
public static string Description(this Enum Value)
{
FieldInfo FI = Value.GetType().GetField(Value.ToString());
IEnumerable<DescriptionAttribute> Attributes = FI.GetCustomAttributes(typeof(DescriptionAttribute), false).Cast<DescriptionAttribute>();
return (Attributes.Any()) ? Attributes.First().Description : Value.ToString();
}
}
....
MyEnum EnumVar = MyEnum.ValueA;
string Description = EnumVar.Description();
can do something like this :
public static class Ext {
public static string ToMyString(this Enumer en ) {
return ((int)en).ToString("00");
}
}
and after use this like:
public enum TYPE { One = 1, Two = 2, Three = 3 }
Type t = TYPE.One;
string s = t.ToMyString();
Yes, conceptually it's the same as like declaring a string , but it's hidden inside extension method.
Other solution is: to simply avoid, at this point, using enums in that way.
Don't use enums for that, but something like a Dictionary or Hash. Enums are there when there is a limited set of possibilities and you do not want or need a value. How it is stored is irrelevant.