Deserializing Enums C#
Is is possible to dynamically deserialize my enum string into enums?
If you are building a Web API project on dotnet-core, the solution in the blog you linked to works great, although I would use the standard System.Text.Json
library (since core 3.0):
using System.Text.Json.Serialization;
namespace Foobar
{
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum Foo
{
Bar = 0,
Baz = 1
}
}
This will convert the enum Foo.Bar
into "Bar"
on the client, and "Bar" -> Foo.Bar
on the server.
Is there a way to keep my enums separate? I feel like a super long list of enum error codes is not very clean or maintainable.
I don't know if I can answer this part yet, because it looks like you've already done that with your class structure. I am confused about whether the code you gave is your goal or your current code.
For more on serialization and deserialization of enums, check out this post.
deserialize json with array of enum
TL/DR: You have two basic problems here:
.NET Core 3.0+ has a new built-in JSON serializer
System.Text.Json
, and you are mixing up attributes and classes between this new serializer and Json.NET. This is very easy to do when both are installed because they share some class names, such asJsonSerializer
andJsonConverter
.The new serializer is used by default but does not yet support serialization of enums as strings with custom value names; see System.Text.Json: How do I specify a custom name for an enum value? for details.
The easiest way to solve your problem is to switch back to Json.NET as shown here and use attributes, converters and namespaces exclusively from this serializer.
First let's break down the differences and similarities between the two serializers:
System.Text.Json
:Built into .NET Core 3.0+ automatically and used for JSON serialization by ASP.NET Core 3.0+ by default.
Namespaces
System.Text.Json
andSystem.Text.Json.Serialization
.Classes including
System.Text.Json.Serialization.JsonConverter
,System.Text.Json.Serialization.JsonConverter<T>
andSystem.Text.Json.JsonSerializer
.Attributes including
System.Text.Json.Serialization.JsonPropertyNameAttribute
,System.Text.Json.Serialization.JsonConverterAttribute
andSystem.Text.Json.Serialization.JsonExtensionDataAttribute
.Serialization of enums as strings is supported by
System.Text.Json.Serialization.JsonStringEnumConverter
, however renaming via attributes is not implemented.See this answer to System.Text.Json: How do I specify a custom name for an enum value? for potential workarounds.
Json.NET:
A 3rd-party library that can be used for serialization in ASP.NET Core 3.0+ by adding a NuGet reference to
Microsoft.AspNetCore.Mvc.NewtonsoftJson
and then callingAddNewtonsoftJson()
inStartup.ConfigureServices
.For details see this answer to Where did IMvcBuilder AddJsonOptions go in .Net Core 3.0? by poke.
Namespaces including
Newtonsoft.Json
,Newtonsoft.Json.Converters
,Newtonsoft.Json.Linq
andNewtonsoft.Json.Serialization
among others.Classes including
Newtonsoft.Json.JsonConverter
,Newtonsoft.Json.JsonConverter<T>
andNewtonsoft.Json.JsonSerializer
Attributes including
Newtonsoft.Json.JsonPropertyAttribute
,Newtonsoft.Json.JsonConverterAttribute
andNewtonsoft.Json.JsonExtensionDataAttribute
among others.Serialization of enums as renamed strings is supported automatically by
Newtonsoft.Json.Converters.StringEnumConverter
when theEnumMemberAttribute
attribute is applied.
With this in mind, which serializer are you using in your code? Since you helpfully included the namespaces in your question, we can check:
using System.Text.Json.Serialization; // System.Text.Json
using Newtonsoft.Json; // Json.NET
namespace Assignment_1
{
public class MyRequest
{
//...
[JsonProperty( // JsonProperty from Newtonsoft
"changeTypes",
ItemConverterType = typeof(JsonStringEnumConverter)// JsonStringEnumConverter from System.Text.Json
)]
public AppGlobals.BoardSymbols[] GameBoard { get; set; }
}
}
So as you can see, you are mixing up attributes from Newtonsoft with converters from System.Text.Json
, which isn't going to work. (Perhaps you selected the namespaces from a "Resolve -> using ..." right-click in Visual Studio?)
So, how to resolve the problem? Since Json.NET supports renaming of enum values out of the box, the easiest way to resolve your problem is to use this serializer. While possibly not as performant as System.Text.Json
it is much more complete and full-featured.
To do this, remove the namespaces System.Text.Json.Serialization
and System.Text.Json
and references to the type JsonStringEnumConverter
from your code, and modify MyRequest
and BoardSymbols
as follows:
using System.Runtime.Serialization;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json;
namespace Assignment_1
{
public class MyRequest
{
//...
[Required]
[MinLength(9)]
[MaxLength(9)]
[JsonProperty("changeTypes")] // No need to add StringEnumConverter here since it's already applied to the enum itself
public AppGlobals.BoardSymbols[] GameBoard { get; set; }
}
}
namespace AppGlobals
{
[JsonConverter(typeof(StringEnumConverter))]
public enum BoardSymbols
{
[EnumMember(Value = "X")]
First = 'X',
[EnumMember(Value = "O")]
Second = 'O',
[EnumMember(Value = "?")]
EMPTY = '?'
}
}
Then NuGet Microsoft.AspNetCore.Mvc.NewtonsoftJson
and in Startup.ConfigureServices
call AddNewtonsoftJson()
:
services.AddMvc()
.AddNewtonsoftJson();
Or if you prefer to use StringEnumConverter
globally:
services.AddMvc()
.AddNewtonsoftJson(o => o.SerializerSettings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter()));
Do take note of the following comment from the docs
Note: If the
AddNewtonsoftJson
method isn't available, make sure that you installed the Microsoft.AspNetCore.Mvc.NewtonsoftJson package. A common error is to install the Newtonsoft.Json package instead of the Microsoft.AspNetCore.Mvc.NewtonsoftJson package.
Mockup fiddle here.
Json.NET serialize ListSystem.Enum and back to concrete types
The MainTheme as a simple string cannot be converted to an Enum. You should parse it using something like
BonusMusic enum = (BonusMusic)Enum.Parse(typeof(BonusMusic), "MainTheme");
Maybe you can implement a JSON custom serializer that could parse the values that come in the Bonuses array
You can check this JSON.NET Implementing Custom Serialization
Regards.
How do I deserialize an array of enum using Json.Net?
The StringEnumConverter
expects only a single enumeration value. Because ChangeTypes
is an array, you need to annotate the property a little differently to make it work.
Try this instead:
[JsonProperty("changeTypes", ItemConverterType=typeof(StringEnumConverter))]
public ChangeType[] ChangeTypes { get; set; }
Deserialize a JSON string integer to an Enum in ASP.NET Core
From How to customize property names and values with System.Text.Json - Enum as string.
By default, System.Text.Json convert enum from integer, but in you case "0" is a string. You need specify a converter to convert enum from string like :
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers().AddJsonOptions(o =>
{
o.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
o.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
});
}
JavaScriptSerializer - JSON serialization of enum as string
No there is no special attribute you can use. JavaScriptSerializer
serializes enums
to their numeric values and not their string representation. You would need to use custom serialization to serialize the enum
as its name instead of numeric value.
If you can use JSON.Net instead of JavaScriptSerializer
than see answer on this question provided by Omer Bokhari: JSON.net covers this use case (via the attribute [JsonConverter(typeof(StringEnumConverter))]
) and many others not handled by the built in .net serializers. Here is a link comparing features and functionalities of the serializers.
JSON deserialization with enum types
Your JSON properties are all strings and so they can only be deserialized to a String
, while Enum
values are actually integers.
You should be able to change your JSON to the following and it'll deserialize just fine
{
"Items": [
{
"ID": "1",
"Name": "Basic Sword",
"ItemType": 2,
"SlotType": 8
},
{
"ID": "2",
"Name": "Advanced Sword",
"ItemType": 2,
"SlotType": 8
},
{
"ID": "3",
"Name": "Leather Chest",
"ItemType": 1,
"SlotType": 2
}
]}
Update
At the time of writing this it had slipped my mind that StringEnumConverter
existed. If you would like to retain readable names in your JSON model
[Serializable]
public class EquipementItem
{
public enum ItemTypes
{
None,
Armor,
Weapon
}
public enum SlotTypes
{
Head,
Shoulders,
Chest,
Bracers,
Gloves,
Waist,
Legs,
Boots,
Weapon
}
public int ID { get; set; }
public string Name { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
public ItemTypes ItemType { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
public SlotTypes SlotType { get; set; }
}
Newtonsoft JSON of Dictionary to list of different Enums
Newtonsoft serializes Enum
default to their numeric value. And then it has no idea how to serialize that numeric value back to Enum
. You can use an attribute on your Enum
to serialize them to string value (so that your data transfer object is understandable) but to deserialize back to an object you have to implement your own JsonConverter
.
It's easier to work with more easily serializable data types.
C# Enum deserialization with Json.Net: Error converting value to type
Let's say we have the following json string:
[
{
"Name": "abc",
"MyEnumValue": "Type1"
},
{
"Name": "abcd",
"MyEnumValue": "Type2"
},
{
"Name": "abcde",
"MyEnumValue": "Type3"
} ,
{
"Name": "abcdef",
"MyEnumValue": "Type4"
}
]
and the following class and enum:
public class MyClass
{
public string Name { get; set; }
public MyEnum MyEnumValue { get; set; }
}
public enum MyEnum
{
Type1,
Type2,
Type3
}
As it can be noticed, the json string array contains item (the last one), that cannot be correctly mapped to the MyEnum
. To avoid deserialization errors you can use the following code snippet:
static void Main(string[] args)
{
var serializationSettings = new JsonSerializerSettings
{
Error = HandleDeserializationError
};
var lst = JsonConvert.DeserializeObject<List<MyClass>>(jsonStr, serializationSettings);
}
public static void HandleDeserializationError(object sender, ErrorEventArgs errorArgs)
{
errorArgs.ErrorContext.Handled = true;
var currentObj = errorArgs.CurrentObject as MyClass;
if (currentObj == null) return;
currentObj.MyEnumValue = MyEnum.Type2;
}
where the jsonStr
variable is the posted json string above. In the above code sample, if MyEnumValue
cannot be correctly interpreted, it is set to a default value of Type2
.
Example: https://dotnetfiddle.net/WKd2Lt
Related Topics
C# Pass Lambda Expression as Method Parameter
Accelerating Bitmap Grayscale Conversion, Is Openmp an Option in C#
What Is the Command to Exit a Console Application in C#
Linq Aggregate and Group by Periods of Time
Is [Callermembername] Slow Compared to Alternatives When Implementing Inotifypropertychanged
How to Make My Windows Form App Snap to Screen Edges
Rx: How to Respond Immediately, and Throttle Subsequent Requests
How to Suppress a Dialog Box Displayed by Code That I Can't Change
Why Re-Initiate the Dbcontext When Using the Entity Framework
Entity Framework - Invalid Column Name '*_Id"
Why Is Addrange Faster Than Using a Foreach Loop
Resharper Conventions for Names of Event Handlers
Validating for Large Files Upon Upload
Embedding a File Explorer Instance in a Windows Forms Application Form
Do Interfaces Derive from System.Object? C# Spec Says Yes, Eric Says No, Reality Says No