JavaScriptSerializer-枚举的JSON序列化为字符串

这篇博客讨论了如何在C#中使用JavaScriptSerializer将枚举序列化为字符串,而非整数值。作者列举了多种解决方案,包括使用Json.NET、自定义转换器和.NET Core的内置方法。博客中还提供了示例代码和注意事项,特别是在处理枚举的扩展和反序列化时的兼容性问题。
摘要由CSDN通过智能技术生成

我有一个包含enum属性的类,并在使用JavaScriptSerializer序列化对象后,我的json结果包含枚举的整数值,而不是其string “ name”。 有没有一种方法可以在我的json中将枚举作为string获取而无需创建自定义JavaScriptConverter ? 也许有一个属性可以修饰enum定义或对象属性?

举个例子:

enum Gender { Male, Female }

class Person
{
    int Age { get; set; }
    Gender Gender { get; set; }
}

所需的json结果:

{ "Age": 35, "Gender": "Male" }

理想情况下,使用内置的.NET框架类寻找答案,如果可能的话,欢迎使用替代方法(如Json.net)。


#1楼

这个版本的Stephen的答案不会更改JSON中的名称:

[DataContract(
    Namespace = 
       "http://schemas.datacontract.org/2004/07/Whatever")]
class Person
{
    [DataMember]
    int Age { get; set; }

    Gender Gender { get; set; }

    [DataMember(Name = "Gender")]
    string GenderString
    {
        get { return this.Gender.ToString(); }
        set 
        { 
            Gender g; 
            this.Gender = Enum.TryParse(value, true, out g) ? g : Gender.Male; 
        }
    }
}

#2楼

这是一个简单的解决方案,它将服务器端C#枚举序列化为JSON,并使用结果填充客户端<select>元素。 这适用于简单的枚举和位标记枚举。

我已经包括了端到端解决方案,因为我认为大多数想要将C#枚举序列化为JSON的人也可能会使用它来填充<select>下拉列表。

开始:

示例枚举

public enum Role
{
    None = Permission.None,
    Guest = Permission.Browse,
    Reader = Permission.Browse| Permission.Help ,
    Manager = Permission.Browse | Permission.Help | Permission.Customise
}

一个复杂的枚举,它使用按位OR生成权限系统。 因此,不能将简单索引[0,1,2 ..]用作枚举的整数值。

服务器端-C#

Get["/roles"] = _ =>
{
    var type = typeof(Role);
    var data = Enum
        .GetNames(type)
        .Select(name => new 
            {
                Id = (int)Enum.Parse(type, name), 
                Name = name 
            })
        .ToArray();

    return Response.AsJson(data);
};

上面的代码使用NancyFX框架来处理Get请求。 它使用了Nancy的Response.AsJson()帮助器方法-但是不用担心,您可以使用任何标准的JSON格式化程序,因为该枚举已经被投影为可以进行序列化的简单匿名类型。

生成的JSON

[
    {"Id":0,"Name":"None"},
    {"Id":2097155,"Name":"Guest"},
    {"Id":2916367,"Name":"Reader"},
    {"Id":4186095,"Name":"Manager"}
]

客户端-CoffeeScript

fillSelect=(id, url, selectedValue=0)->
    $select = $ id
    $option = (item)-> $ "<option/>", 
        {
            value:"#{item.Id}"
            html:"#{item.Name}"
            selected:"selected" if item.Id is selectedValue
        }
    $.getJSON(url).done (data)->$option(item).appendTo $select for item in data

$ ->
    fillSelect "#role", "/roles", 2916367

HTML之前

<select id="role" name="role"></select>

HTML之后

<select id="role" name="role">
    <option value="0">None</option>
    <option value="2097155">Guest</option>
    <option value="2916367" selected="selected">Reader</option>
    <option value="4186095">Manager</option>
</select>

#3楼

将以下内容添加到global.asax中,以将c#枚举的JSON序列化为字符串

  HttpConfiguration config = GlobalConfiguration.Configuration;
            config.Formatters.JsonFormatter.SerializerSettings.Formatting =
                Newtonsoft.Json.Formatting.Indented;

            config.Formatters.JsonFormatter.SerializerSettings.Converters.Add
                (new Newtonsoft.Json.Converters.StringEnumConverter());

#4楼

我无法像(@ob。)的最高答案中那样更改源模型,也不想像@Iggy这样在全球范围内注册它。 所以我将https://stackoverflow.com/a/2870420/237091和@Iggy的https://stackoverflow.com/a/18152942/237091结合在一起,以允许在SerializeObject命令本身期间设置字符串枚举转换器:

Newtonsoft.Json.JsonConvert.SerializeObject(
    objectToSerialize, 
    Newtonsoft.Json.Formatting.None, 
    new Newtonsoft.Json.JsonSerializerSettings()
    {
        Converters = new List<Newtonsoft.Json.JsonConverter> {
            new Newtonsoft.Json.Converters.StringEnumConverter()
        }
    })

#5楼

不,没有可以使用的特殊属性。 JavaScriptSerializerenums序列化为其数值,而不是其字符串表示形式。 您将需要使用自定义序列化来将enum序列化为其名称而不是数字值。


如果可以使用JSON.Net代替JavaScriptSerializer ,则请参阅OmerBakhari提供的有关此问题答案 :JSON.net涵盖了此用例(通过属性[JsonConverter(typeof(StringEnumConverter))] )以及许多其他内置方法未处理的情况.net序列化器。 这是一个比较串行器特性和功能的链接


#6楼

@Iggy答案仅将ASP.NET(Web API等)的c#枚举的JSON序列化设置为字符串。

但是要使其也可以用于临时序列化,请在您的开始类中添加以下内容(例如Global.asax Application_Start)

//convert Enums to Strings (instead of Integer) globally
JsonConvert.DefaultSettings = (() =>
{
    var settings = new JsonSerializerSettings();
    settings.Converters.Add(new StringEnumConverter { CamelCaseText = true });
    return settings;
});

有关Json.NET页面的更多信息

此外,要让您的枚举成员对特定文本进行序列化/反序列化,请使用

System.Runtime.Serialization.EnumMember

属性,如下所示:

public enum time_zone_enum
{
    [EnumMember(Value = "Europe/London")] 
    EuropeLondon,

    [EnumMember(Value = "US/Alaska")] 
    USAlaska
}

#7楼

我发现Json.NET通过StringEnumConverter属性提供了我正在寻找的确切功能:

using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

[JsonConverter(typeof(StringEnumConverter))]
public Gender Gender { get; set; }

有关更多详细信息,请StringEnumConverter文档

还有其他地方可以更全局地配置此转换器:

  • 枚举本身,如果您想让枚举始终被序列化/反序列化为字符串:

     [JsonConverter(typeof(StringEnumConverter))] enum Gender { Male, Female } 
  • 如果有人想避免装修的属性,你可以转换器添加到您的JsonSerializer(所建议比约恩埃吉尔 ):

     serializer.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter()); 

    并且它将对在序列化期间看到的每个枚举都有效(由Travis建议)。

  • 或JsonConverter(由香蕉建议):

     JsonConvert.SerializeObject(MyObject, new Newtonsoft.Json.Converters.StringEnumConverter()); 

另外,您可以使用StringEnumConverter(NamingStrategy,Boolean)构造函数来控制大小写以及是否仍接受数字。


#8楼

这是newtonsoft.json的答案

enum Gender { Male, Female }

class Person
{
    int Age { get; set; }

    [JsonConverter(typeof(StringEnumConverter))]
    Gender Gender { get; set; }
}

#9楼

您可以通过调用JsonConverter.SerializeObject来创建JsonSerializerSettings,如下所示:

var result = JsonConvert.SerializeObject
            (
                dataObject,
                new JsonSerializerSettings
                {
                    Converters = new [] {new StringEnumConverter()}
                }
            );

#10楼

注意,当存在Description属性时,没有序列化答案。

这是我支持Description属性的实现。

public class CustomStringEnumConverter : Newtonsoft.Json.Converters.StringEnumConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        Type type = value.GetType() as Type;

        if (!type.IsEnum) throw new InvalidOperationException("Only type Enum is supported");
        foreach (var field in type.GetFields())
        {
            if (field.Name == value.ToString())
            {
                var attribute = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute;
                writer.WriteValue(attribute != null ? attribute.Description : field.Name);

                return;
            }
        }

        throw new ArgumentException("Enum not found");
    }
}

枚举:

public enum FooEnum
{
    // Will be serialized as "Not Applicable"
    [Description("Not Applicable")]
    NotApplicable,

    // Will be serialized as "Applicable"
    Applicable
}

用法:

[JsonConverter(typeof(CustomStringEnumConverter))]
public FooEnum test { get; set; }

#11楼

您还可以将转换器添加到您的JsonSerializer如果你不想使用JsonConverter属性:

string SerializedResponse = JsonConvert.SerializeObject(
     objToSerialize, 
     new Newtonsoft.Json.Converters.StringEnumConverter()
); 

它适用于在序列化期间看到的每个enum


#12楼

new JavaScriptSerializer().Serialize(  
    (from p   
    in (new List<Person>() {  
        new Person()  
        {  
            Age = 35,  
            Gender = Gender.Male  
        }  
    })  
    select new { Age =p.Age, Gender=p.Gender.ToString() }  
    ).ToArray()[0]  
);

#13楼

以防万一有人发现上述不足,我最终解决了这个过载:

JsonConvert.SerializeObject(objToSerialize, Formatting.Indented, new Newtonsoft.Json.Converters.StringEnumConverter())

#14楼

我已经使用Newtonsoft.Json库将这个解决方案的所有部分放在一起。 它解决了枚举问题,并使错误处理变得更好,并且可以在IIS托管服务中使用。 它的代码很多,因此您可以在GitHub上找到它: https : //github.com/jongrant/wcfjsonserializer/blob/master/NewtonsoftJsonFormatter.cs

您必须在Web.config添加一些条目才能使其正常工作,您可以在此处查看示例文件: https : //github.com/jongrant/wcfjsonserializer/blob/master/Web.config


#15楼

对于.Net Core:-

public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddJsonFormatters(f => f.Converters.Add(new StringEnumConverter()));
    ...
}

#16楼

Omer Bokhari和uri的答案的结合始终是我的解决方案,因为我想要提供的值通常不同于枚举中所包含的值,特别是如果需要,我希望能够更改枚举。

因此,如果有人感兴趣,它是这样的:

public enum Gender
{
   [EnumMember(Value = "male")] 
   Male,
   [EnumMember(Value = "female")] 
   Female
}

class Person
{
    int Age { get; set; }
    [JsonConverter(typeof(StringEnumConverter))]
    Gender Gender { get; set; }
}

#17楼

ASP.NET Core方式:

public class Startup
{
  public IServiceProvider ConfigureServices(IServiceCollection services)
  {
    services.AddMvc().AddJsonOptions(options =>
    {
      options.SerializerSettings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter());
    });
  }
}

https://gist.github.com/regisdiogo/27f62ef83a804668eb0d9d0f63989e3e


#18楼

对于ASP.Net核心,只需将以下内容添加到您的启动类中:

JsonConvert.DefaultSettings = (() =>
        {
            var settings = new JsonSerializerSettings();
            settings.Converters.Add(new StringEnumConverter { AllowIntegerValues = false });
            return settings;
        });

#19楼

不知道这是否仍然有意义,但是我不得不直接写一个json文件,然后我想到了以下将几个stackoverflow答案拼凑在一起的方法

public class LowercaseJsonSerializer
{
    private static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
    {
        ContractResolver = new LowercaseContractResolver()
    };

    public static void Serialize(TextWriter file, object o)
    {
        JsonSerializer serializer = new JsonSerializer()
        {
            ContractResolver = new LowercaseContractResolver(),
            Formatting = Formatting.Indented,
            NullValueHandling = NullValueHandling.Ignore
        };
        serializer.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter());
        serializer.Serialize(file, o);
    }

    public class LowercaseContractResolver : DefaultContractResolver
    {
        protected override string ResolvePropertyName(string propertyName)
        {
            return Char.ToLowerInvariant(propertyName[0]) + propertyName.Substring(1);
        }
    }
}

它确保我所有的json键都是按照json“规则”开头的小写字母。 格式化它的缩进格式,并忽略输出中的null。 Aslo通过添加StringEnumConverter来打印带有其字符串值的枚举。

我个人认为这是我能想到的最简洁的方法,而不必用注释弄脏模型。

用法:

    internal void SaveJson(string fileName)
    {
        // serialize JSON directly to a file
        using (StreamWriter file = File.CreateText(@fileName))
        {
            LowercaseJsonSerializer.Serialize(file, jsonobject);
        }
    }

#20楼

对于VB.net,我发现以下作品:

Dim sec = New Newtonsoft.Json.Converters.StringEnumConverter()
sec.NamingStrategy() = New Serialization.CamelCaseNamingStrategy

Dim JSON_s As New JsonSerializer
JSON_s.Converters.Add(sec)

Dim jsonObject As JObject
jsonObject = JObject.FromObject(SomeObject, JSON_s)
Dim text = jsonObject.ToString

IO.File.WriteAllText(filePath, text)

#21楼

稍具前瞻性的选择

面对相同的问题,我们确定需要StringEnumConverter的自定义版本,以确保我们的枚举值可以随时间扩展而不会在反序列化方面造成灾难性的破坏(请参见下面的背景)。 使用下面的SafeEnumConverter允许反序列化完成,即使有效负载包含的枚举值没有命名定义,也更接近于int枚举转换的工作方式。

用法:

[SafeEnumConverter]
public enum Colors
{
    Red,
    Green,
    Blue,
    Unsupported = -1
}

要么

[SafeEnumConverter((int) Colors.Blue)]
public enum Colors
{
    Red,
    Green,
    Blue
}

资源:

public class SafeEnumConverter : StringEnumConverter
{
    private readonly int _defaultValue;

    public SafeEnumConverter()
    {
        // if you've been careful to *always* create enums with `0` reserved
        // as an unknown/default value (which you should), you could use 0 here. 
        _defaultValue = -1;
    }

    public SafeEnumConverter(int defaultValue)
    {
        _defaultValue = defaultValue;
    }

    /// <summary>
    /// Reads the provided JSON and attempts to convert using StringEnumConverter. If that fails set the value to the default value.
    /// </summary>
    /// <returns>The deserialized value of the enum if it exists or the default value if it does not.</returns>
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        try
        {
            return base.ReadJson(reader, objectType, existingValue, serializer);
        }
        catch
        {
            return Enum.Parse(objectType, $"{_defaultValue}");
        }
    }

    public override bool CanConvert(Type objectType)
    {
        return base.CanConvert(objectType) && objectType.GetTypeInfo().IsEnum;
    }
}

背景

当我们使用StringEnumConverter ,我们遇到的问题是,在添加新的枚举值的情况下,我们也需要被动性,但并不是每个客户都立即意识到新值。 在这些情况下,与Newtonsoft JSON打包在一起的StringEnumConverter抛出一个JsonSerializationException类似于“将值SomeString转换为EnumType类型时出错”,然后整个反序列化过程将失败。 这对我们来说是一个交易突破,因为即使客户计划忽略/丢弃它不了解的属性值,它仍然需要能够反序列化其余的有效负载!


#22楼

在.net core 3中,现在可以通过System.Text.Json中的内置类来实现:

var person = new Person();
// Create and add a converter which will use the string representation instead of the numeric value.
var stringEnumConverter = new System.Text.Json.Serialization.JsonStringEnumConverter();
JsonSerializerOptions opts = new JsonSerializerOptions();
opts.Converters.Add(stringEnumConverter);
// Generate json string.
var json = JsonSerializer.Serialize<Person>(person, opts);

要为JsonStringEnumConverter配置特定属性的属性装饰:

using System.Text.Json.Serialization;

[JsonConverter(typeof(JsonStringEnumConverter))]
public Gender Gender { get; set; }

如果要始终将枚举转换为字符串,请将属性放在枚举本身。

[JsonConverter(typeof(JsonStringEnumConverter))] 
enum Gender { Male, Female }

#23楼

具有System.Text.Json的Asp.Net Core 3

public void ConfigureServices(IServiceCollection services)
{

    services
        .AddControllers()
        .AddJsonOptions(options => 
           options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter())
        );

    //...
 }

#24楼

实际上,您可以使用JavaScriptConverter通过内置的JavaScriptSerializer来完成此操作。 通过将枚举转换为Uri,可以将其编码为字符串。

我已经描述了如何针对日期执行此操作,但是它也可以用于枚举。 .NET JavaScriptSerializer的自定义DateTime JSON格式


#25楼

这是很容易通过添加完成ScriptIgnore属性的Gender属性,使其不被序列化,并增加了GenderString不会获取序列化属性:

class Person
{
    int Age { get; set; }

    [ScriptIgnore]
    Gender Gender { get; set; }

    string GenderString { get { return Gender.ToString(); } }
}

#26楼

这是一个老问题,但我想我会为以防万一。 在我的项目中,我对任何Json请求使用单独的模型。 模型通常与带有“ Json”前缀的域对象具有相同的名称。 使用AutoMapper映射模型。 通过让json模型声明一个字符串属性,该属性是域类的枚举,AutoMapper将解析为它的字符串表示形式。

如果您想知道,我需要为Json序列化类提供单独的模型,因为内置的序列化程序会附带循环引用。

希望这对某人有帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值