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


1160

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

举个例子:

enum Gender { Male, Female }

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

所需的json结果:

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

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


8
改到哪个?投票支持率最高的答案实际上并不能回答问题-是的,它在其他情况下很有用,因此在投票中很有用,但是如果您坚持使用MS JavaScriptSerializer,则无论如何实际上都没有实际用处,因为使用页面方法和,最重要的是,根据问题的要求。接受的答案说不可能。我的回答虽然有点骇人听闻,却能完成工作。
斯蒂芬·肯尼迪

Answers:


374

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


如果您可以使用JSON.Net而不是JavaScriptSerializer不愿看到 关于这个问题的答案提供OmerBakhari:JSON.net涵盖这种使用情况下(通过属性[JsonConverter(typeof(StringEnumConverter))])和许多其他.NET序列化没有内置处理。这是一个比较串行器特性和功能的链接


7
@Fabzter-使用Newtonsoft的Json
BeemerGuy,

1
@BornToCode Json.NET是ASP.NET默认使用的序列化程序。
BrainSlugs83 2013年

12
@ BrainSlugs83-问题是关于使用JavaScriptSerializer,而不是Json.NET(如果查看修订历史记录,您会发现有一个编辑说明这一点),如果使用JavaScriptSerializer,则该属性JsonConverter将无法使用。
BornToCode

50
请将其删除为可接受的答案,因为它不能解决问题,下面的答案带有1000多个支持。
MHGameWork

你能回答我吗
陈永强

2100

我发现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)构造函数来控制大小写以及是否仍接受数字。


9
通过链接了解如何在asp.net mvc应用程序james.newtonking.com/archive/2008/10/16/…中
RredCat 2010年


61
HttpConfiguration config = GlobalConfiguration.Configuration; config.Formatters.JsonFormatter.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.Indented; config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(新的Newtonsoft.Json.Converters.StringEnumConverter());
Iggy 2013年

1
值得注意的是,默认情况下ASP.NET MVC不使用Json.Net作为json序列化程序,因此需要扩展Controller或手动覆盖每个序列化。
奥德斯2014年

2
您可以自定义转换器(例如,用于camelCase输出):new StringEnumConverter { CamelCaseText = true }
Seafish

172

将以下内容添加到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
由于某些原因,我没有使它起作用。Fiddler表现出固执的2而不是“警告”,即使这样做也是如此。另外-为何将更Formatting改为Indented
sq33G 2013年

5
此示例的第三行已添加到App_start / webapiconfig.cs文件中,并在ASP.NET Web API 2.1项目中为我提供了一个技巧,以返回REST(json fomat)调用中枚举值的字符串。
Greg Z.14年

1
有没有一种方法可以仅按请求范围设置此属性?
Anestis Kivranoglou's

@AnestisKivranoglou只是对每个请求使用自己的设置使用自定义json序列化程序。
BrainSlugs83 2013年

3
缩进的第一个序列化程序设置与op问题无关。
user3791372 '16

153

@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
}

6
谢谢!我只是在寻找[EnumMember]
Poulad

CamelCaseText属性现在标记为过时。实例化转换器的新方法:new StringEnumConverter(new CamelCaseNamingStrategy())
菲亚特

非常感谢,让我开心!:)
Eldoïr

39

我无法像(@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()
        }
    })

这也炒菜很好,如果你有这样的List <someEnumType>属性
波格丹

34

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; }
}

1
我使用的JsonPropertyAttribute是枚举成员,它可用于简单的反序列化任务。可悲的是,在使用JTokens 进行手动调整时,它会被忽略。Happilly EnumMemberAttribute的魅力十足。谢谢!
Prolog

可以用JavaScriptSerializer吗?
肯尼迪

31

通过向ScriptIgnore属性添加一个属性Gender,使其不被序列化,并添加一个确实被序列化的属性,可以很容易地做到GenderString一点

class Person
{
    int Age { get; set; }

    [ScriptIgnore]
    Gender Gender { get; set; }

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

29
让我尝试解释一下。根据设计人员的说法,此解决方案不正确。您已根据查看目的修改了模型。但是模型必须仅包含数据,并且不关心表示。您必须在另一层上移动此功能。
RredCat

4
实际上,模型是用来传递来自控制器的数据的,而正是控制器,它并不关心表示。引入自动化属性(此处为GenderString)不会破坏控制器,该控制器仍使用Gender属性,但可轻松访问视图。逻辑解决方案。
Dima

17
@RredCat在“视图模型”中具有特定于视图的属性没有错。恕我直言,这个错误将不会从域模型视图模型分为:blogs.msdn.com/b/simonince/archive/2010/01/26/...
马里亚诺Desanze

5
@RredCat,即使根据某种模式它是不正确的,OP也对此没有说明,因此这确实是正确的答案。(即使我在哲学上同意您的观点。)
MEMark

10
在这个评论中,荒谬的自行车脱落令人着迷。
Mike Mooney 2015年

26

这个版本的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; 
        }
    }
}

3
我相信这对DataContractJsonSerializer不是这样的人是有效的JavaScriptSerializer
KCD 2012年

使用本机.NET Framework序列化程序为我解决问题并为我解决。
参议员

对我来说是最佳解决方案,因为我不允许使用第三方库(ISO遵从性问题)
Daniel Gruszczyk

当然,这不是针对序列化器的类型。JavaScriptSerializer序列化所有不被忽略的内容,而DataContractJsonSerializer需要DataMember属性。感谢您的呼喊,但请注意,您拼错了我的名字:)
Stephen Kennedy

25

这是newtonsoft.json的答案

enum Gender { Male, Female }

class Person
{
    int Age { get; set; }

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

1
谢谢您的回答,对我帮助很大!如果要在PascalCase中定义枚举,但希望在camelCase中进行序列化,则需要true像这样添加到JsonConverter类型:[JsonConverter(typeof(StringEnumConverter), true)]
Peet


16

JsonSerializer如果您不想使用JsonConverter属性,也可以在其中添加一个转换器:

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

它会enum在序列化期间看到的所有内容都起作用。


15

这是一个简单的解决方案,它将服务器端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请求。它使用南希的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>

13

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

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

1
这适用于所有版本,而不仅仅是核心版本。
bikeman868

11

您可以通过调用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; }

10

对于.Net Core:-

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

2
如果这是Microsoft.AspNetCore.Mvc.Formatters.JsonNuGet包中的方法,则似乎只是上的一种扩展方法IMvcCoreBuilder,而不是IMvcBuilder。所以它的使用一样services.AddMvcCore().AddJsonFormatters(f => f.Converters.Add(new StringEnumConverter()));
infl3x 17-10-27

9

在.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 }

9

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

public void ConfigureServices(IServiceCollection services)
{

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

    //...
 }

8

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

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

对于我当前的用例,这是一个很好的解决方案:我不想更改序列化程序的默认值,并且在使用属性时遇到问题,因为我的属性属于IList <EnumType>类型。
Dirk Brockhaus

5

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

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

希望这对某人有帮助。


很高兴了解Automapper的功能;-) [ScriptIgnore]属性将删除循环引用
ledragon 2013年

1
哦。不知道该属性。谢谢!您会在Pocos上使用它吗?我一直对所有Poco属性使用MetadataType定义,只是为了保持它们的整洁。属性仍然可以通过元数据工作吗?
Ales Potocnik Hahonina


1

不知道这是否仍然有意义,但是我不得不直接写一个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);
        }
    }

0

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

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


0

对于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)

0

稍具前瞻性的选择

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


-2
        Person p = new Person();
        p.Age = 35;
        p.Gender = Gender.Male;
        //1.  male="Male";
        string male = Gender.Male.ToString();

        p.Gender = Gender.Female;

        //2.  female="Female";
        string female = Enum.GetName(typeof(Gender), p.Gender);

        JObject jobj = new JObject();
        jobj["Age"] = p.Age;
        jobj["Gender"] = male;
        jobj["Gender2"] = female;

        //you result:  josn= {"Age": 35,"Gender": "Male","Gender2": "Female"}
        string json = jobj.ToString();

-5
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]  
);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.