【问题标题】:DataContractJsonSerializer and EnumsDataContractJsonSerializer 和枚举
【发布时间】:2023-03-24 06:54:01
【问题描述】:

当我使用 DataContractJsonSerializer 序列化枚举值时,它会序列化枚举的数值,而不是字符串名称。

IE:

enum foo
{
   bar,
   baz
}

序列化 foo.bar 的值返回“0”,而不是“bar”。

我更喜欢它,有没有办法覆盖它?

编辑:

因为我不想更改序列化程序,所以我使用了一个简单的解决方法 hack。

我在类中公开了一个属性来序列化,它在值上调用 ToString,即:

// Old
[DataMember]
public EnumType Foo
{
    get { return _foo; }
    set { _foo = value; }
}

// New, I still kept the EnumType but I only serialize the string version

public EnumType Foo
{
    get { return _foo; }
    set { _foo = value; }
}

[DataMember]
public string FooType
{
    get { return _foo.ToString(); }
    private set {}
}

【问题讨论】:

  • 这并不奇怪,因为枚举默认为 int 类型。
  • 在 XML 序列化中,还建议避免在 wcf 数据协定中公开枚举,因为它们会产生微妙的向后兼容问题。见stackoverflow.com/questions/326339/…

标签: wcf json serialization


【解决方案1】:

It looks like this is by design 并且此行为无法更改:

枚举成员值被处理 作为 JSON 中的数字,这是不同的 从它们在数据中的处理方式 合同,其中它们被包括为 成员名称。

这是一个使用 an alternative(以及 IMO 更好和更可扩展)序列化程序的示例,它可以实现您正在寻找的内容:

using System;
using Newtonsoft.Json;

class Program
{
    static void Main(string[] args)
    {
        var baz = Foo.Baz;
        var serializer = new JsonSerializer();
        serializer.Converters.Add(new JsonEnumTypeConverter());
        serializer.Serialize(Console.Out, baz);
        Console.WriteLine();
    }
}

enum Foo
{
    Bar,
    Baz
}

public class JsonEnumTypeConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(Foo);
    }
    public override void WriteJson(JsonWriter writer, object value)
    {
        writer.WriteValue(((Foo)value).ToString());
    }

    public override object ReadJson(JsonReader reader, Type objectType)
    {
        return Enum.Parse(typeof(Foo), reader.Value.ToString());
    }
}

【讨论】:

  • 我相信新的方法是 jsonSerializer.Converters.Add(new StringEnumConverter()); - 从 4.5 开始
  • @shanabus - 你在 wcf 服务中的什么地方添加了这条神奇的线? (问题被标记为 wcf)
  • @BornToCode 我对wcf 不是很熟悉,这不是我发现这个问题的方式。这是否回答了您的问题 - stackoverflow.com/questions/6642961/…
【解决方案2】:

我疯狂地试图找到一个优雅的解决方案来解决这个问题,因为似乎每个人都默认使用 Newtonsoft 的序列化程序来解决这个问题。 尽管 Newtonsoft 提供了更多功能,但它确实有一些严重的缺点。 举几个例子:需要无参数的构造函数,如果你想序列化实现 IEnumerable 的类的疯狂行为,并且在使用抽象类型时表现非常糟糕(因为它不使用 KnownTypes 属性,解决方法会生成一个将内部命名空间暴露给调用者的详细输出)。

另一方面,在 MVC4 WebApi 解决方案上使用 DataContractJsonSerializer 时如何自定义它的例子很少。

我花了一段时间才找到将枚举表示为字符串并解决 DataContractJsonSerializer 附带的已知 DateTime 格式问题的解决方案。

第 I 部分 - 将这些扩展方法放在扩展类中 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~

#region JSon

    /// <summary>Serializes an object to JSon.</summary>
    /// <param name="obj">The object to serialize.</param>
    /// <returns>Returns a byte array with the serialized object.</returns>
    /// <remarks>This implementation outputs dates in the correct format and enums as string.</remarks>
    [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed.")]
    public static byte[] SerializeJson(this object obj)
    {
        using (MemoryStream b = new MemoryStream())
        {
            SerializeJson(obj, b);
            return b.ToArray();
        }
    }

    /// <summary>Serializes an object to JSon.</summary>
    /// <param name="obj">The object to serialize.</param>
    /// <param name="stream">The stream to write to.</param>
    /// <remarks>This implementation outputs dates in the correct format and enums as string.</remarks>
    [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed.")]
    public static void SerializeJson(this object obj, Stream stream)
    {
        var settings = new DataContractJsonSerializerSettings();
        settings.DateTimeFormat = new System.Runtime.Serialization.DateTimeFormat("yyyy-MM-dd'T'HH:mm:ssZ");
        settings.DataContractSurrogate = new EnumToStringDataContractSurrogate();

        var type = obj == null ? typeof(object) : obj.GetType();

        var enumerationValue = obj as System.Collections.IEnumerable;

        var fixedValue = enumerationValue != null
                         ? type.IsGenericType && !type.GetGenericArguments()[0].IsInterface
                            ? enumerationValue.ToArray(type.GetGenericArguments()[0])
                            : enumerationValue.OfType<object>().ToArray()
                         : obj;

        if (enumerationValue != null && (!type.IsGenericType || (type.IsGenericType || type.GetGenericArguments()[0].IsInterface)))
        {
            var firstMember = (fixedValue as System.Collections.IEnumerable).OfType<object>().FirstOrDefault();
            if (firstMember != null)
                fixedValue = enumerationValue.ToArray(firstMember.GetType());
        }

        var fixedType = obj == null
                        ? type
                        : fixedValue.GetType();

        var jsonSer = new DataContractJsonSerializer(fixedType, settings);
        jsonSer.WriteObject(stream, fixedValue);
    }

    /// <summary>
    /// Deserializes an object.
    /// </summary>
    /// <typeparam name="T">The output type of the object.</typeparam>
    /// <param name="data">The serialized contents.</param>
    /// <returns>Returns the typed deserialized object.</returns>
    /// <remarks>This implementation outputs dates in the correct format and enums as string.</remarks>
    [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "JSon")]
    [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed.")]
    public static T DeserializeJSon<T>(this byte[] data)
    {
        using (MemoryStream b = new MemoryStream(data))
            return DeserializeJSon<T>(b);
    }

    /// <summary>Deserializes a JSon object.</summary>
    /// <typeparam name="T">The output type of the object.</typeparam>
    /// <param name="stream">The stream to read from.</param>
    /// <returns>Returns the typed object.</returns>
    /// <remarks>This implementation outputs dates in the correct format and enums as string.</remarks>
    [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "JSon")]
    [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed.")]
    public static T DeserializeJSon<T>(this Stream stream)
    {
        var settings = new DataContractJsonSerializerSettings();
        settings.DateTimeFormat = new System.Runtime.Serialization.DateTimeFormat("yyyy-MM-dd'T'HH:mm:ssZ");
        settings.DataContractSurrogate = new EnumToStringDataContractSurrogate();

        var jsonSer = new DataContractJsonSerializer(typeof(T), settings);
        return (T)jsonSer.ReadObject(stream);
    }

    /// <summary>Deserializes a JSon object.</summary>
    /// <param name="data">The serialized contents.</param>
    /// <param name="targetType">The target type.</param>
    /// <returns>Returns the typed deserialized object.</returns>
    /// <remarks>This implementation outputs dates in the correct format and enums as string.</remarks>
    [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "JSon")]
    [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed.")]
    public static object DeserializeJSon(this byte[] data, Type targetType)
    {
        using (MemoryStream b = new MemoryStream(data))
        {
            return DeserializeJSon(b, targetType);
        }
    }

    /// <summary>Deserializes a JSon object.</summary>
    /// <param name="data">The serialized contents.</param>
    /// <param name="targetType">The target type.</param>
    /// <returns>Returns the typed deserialized object.</returns>
    /// <remarks>This implementation outputs dates in the correct format and enums as string.</remarks>
    [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "JSon")]
    [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed.")]
    public static object DeserializeJSon(this Stream data, Type targetType)
    {
        var settings = new DataContractJsonSerializerSettings();
        settings.DateTimeFormat = new System.Runtime.Serialization.DateTimeFormat("yyyy-MM-dd'T'HH:mm:ssZ");
        settings.DataContractSurrogate = new EnumToStringDataContractSurrogate();

        var jsonSer = new DataContractJsonSerializer(targetType, settings);
        return jsonSer.ReadObject(data);            
    }

    /// <summary>Enumerator contract surrogate.</summary>
    internal class EnumToStringDataContractSurrogate : IDataContractSurrogate
    {
        Type IDataContractSurrogate.GetDataContractType(Type type)
        {
            return type == typeof(Enum) ? typeof(string) : type;
        }

        object IDataContractSurrogate.GetDeserializedObject(object obj, Type targetType)
        {
            if (targetType.IsEnum)
            {
                return obj == null
                       ? System.Enum.GetValues(targetType).OfType<int>().FirstOrDefault()
                       : System.Enum.Parse(targetType, obj.ToString());
            }
            return obj;
        }

        object IDataContractSurrogate.GetObjectToSerialize(object obj, Type targetType)
        {
            if (obj is Enum)
            {
                var pair = Enum.GetName(obj.GetType(), obj);
                return pair;
            }

            return obj;
        }

        object IDataContractSurrogate.GetCustomDataToExport(Type clrType, Type dataContractType)
        {
            throw new NotImplementedException();
        }

        object IDataContractSurrogate.GetCustomDataToExport(System.Reflection.MemberInfo memberInfo, Type dataContractType)
        {
            throw new NotImplementedException();
        }

        void IDataContractSurrogate.GetKnownCustomDataTypes(System.Collections.ObjectModel.Collection<Type> customDataTypes)
        {
            throw new NotImplementedException();
        }

        Type IDataContractSurrogate.GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
        {
            throw new NotImplementedException();
        }

        System.CodeDom.CodeTypeDeclaration IDataContractSurrogate.ProcessImportedType(System.CodeDom.CodeTypeDeclaration typeDeclaration, System.CodeDom.CodeCompileUnit compileUnit)
        {
            throw new NotImplementedException();
        }
    }

    #endregion


    /// <summary>Creates an array from a non generic source.</summary>
    /// <param name="source">The source.</param>
    /// <param name="type">The target type of the array.</param>
    /// <returns>Returns a typed array.</returns>
    public static Array ToArray(this IEnumerable source, Type type)
    {
        var param = Expression.Parameter(typeof(IEnumerable), "source");
        var cast = Expression.Call(typeof(Enumerable), "Cast", new[] { type }, param);
        var toArray = Expression.Call(typeof(Enumerable), "ToArray", new[] { type }, cast);
        var lambda = Expression.Lambda<Func<IEnumerable, Array>>(toArray, param).Compile();

        return lambda(source);
    }

第二部分 - 通过封装 DataContractJsonSerializer 创建您自己的格式化程序 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

/// <summary>Custom implementation of DataContract formatter.</summary>
public class DataContractJsonFormatter : MediaTypeFormatter
{

    /// <summary>Creates a new instance.</summary>
    public DataContractJsonFormatter()
    {
        SupportedMediaTypes.Add(new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"));
    }

    /// <summary>Gets if the formatter the write a given type.</summary>
    /// <param name="type">The type to handle.</param>
    /// <returns>Returns if the formatter the write a given type.</returns>
    public override bool CanWriteType(Type type)
    {
        return true;
    }

    /// <summary>Gets if the formatter the read a given type.</summary>
    /// <param name="type">The type to handle.</param>
    /// <returns>Returns if the formatter the read a given type.</returns>
    public override bool CanReadType(Type type)
    {
        return true;
    }

    /// <summary>Deserializes an object.</summary>
    /// <param name="type">The target type.</param>
    /// <param name="readStream">The stream to read from.</param>
    /// <param name="content">The http content.</param>
    /// <param name="formatterLogger">A logger.</param>
    /// <returns>Returns the deserialized object.</returns>
    public override Task<object> ReadFromStreamAsync(Type type, Stream readStream, System.Net.Http.HttpContent content, IFormatterLogger formatterLogger)
    {
        var task = Task<object>.Factory.StartNew(() =>
        {
            return readStream.DeserializeJSon(type);
        });

        return task;
    }

    /// <summary>Serializes an object.</summary>
    /// <param name="type">The target type.</param>
    /// <param name="value">The object to serialize.</param>
    /// <param name="writeStream">The stream to write to.</param>
    /// <param name="content">The http content.</param>
    /// <param name="transportContext">The context.</param>
    /// <returns>Returns the deserialized object.</returns>
    public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, System.Net.Http.HttpContent content, System.Net.TransportContext transportContext)
    {
        var task = Task.Factory.StartNew(() =>
        {
            value.SerializeJson(writeStream);
        });

        return task;
    }
}

第三部分 - 编辑您的 Global.asax 文件并使用您的新 JSon 格式化程序 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~

    /// <summary>Event handlers of when the application starts.</summary>
    [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
    protected void Application_Start()
    {
       //Register my custom DataContract JSon serializer
        GlobalConfiguration.Configuration.Formatters.Insert(0, new DataContractJsonFormatter());

        //Register areas
        AreaRegistration.RegisterAllAreas();

        WebApiConfig.Register(GlobalConfiguration.Configuration);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
       // BundleConfig.RegisterBundles(BundleTable.Bundles);

        //JSON serialization config
        var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
        json.UseDataContractJsonSerializer = false;
    }

【讨论】:

    【解决方案3】:

    要获得 wcf json 的 2 路序列化/反序列化,您可以添加第二个字符串类型的 get set 属性,当您在 javascript 中构建 json 对象时,请使用 string 命名属性,在服务器端使用强类型枚举版本:eg

    public class DTOSearchCriteria {
      public int ? ManufacturerID { get; set; }
      public int ? ModelID { get; set; }
    
      private SortBy _sort;
    
      public SortBy SortType {
        get { return _sort; }
        set { _sort = value; }
      }
    
      public String Sort {
        get {
          return _sort.ToString();
        }
        set {
          _sort = (SortBy) Enum.Parse(typeof(SortBy), value);
        }
      }
    
      public int PageSize { get; set; }
      public int PageNumber { get; set; }
    }
    
    public enum SortBy {
      PriceDescending,
      PriceAscending
    }
    

    【讨论】:

    • 虽然丑得要命。微软强迫这种类型的实施真是太丢脸了。
    【解决方案4】:

    编辑:抱歉刚起床没喝咖啡:(

    这是使用 Json 序列化器而不是 DataContractJsonSerializer 执行您想要执行的操作的代码。

    我还没有使用 DataContractJsonSerializer 完成任何工作,但是在快速扫描文档后,我对 MS 相当失望。他们显然走极端使 WCF 非常可扩展,但是使用 DataContractJsonSerializer 没有可扩展性。你必须使用 MS 风格的 JSON。 MS 的超级蹩脚......已经搞砸了 WCF。

        using System;
        using System.Collections.Generic;
        using System.Runtime.Serialization;
        using System.Web.Script.Serialization;
    

    一些测试对象和枚举:

        public enum SomeSillyEnum
        {
            Foo,Bar,Doo,Daa,Dee
        }
    
        public class UseSillyEnum
        {
            public SomeSillyEnum PublicEnum { get; set; }
            public string SomeOtherProperty { get; set; }
            public UseSillyEnum()
            {
                PublicEnum = SomeSillyEnum.Foo;
                SomeOtherProperty = "Testing";
            }
        }
    

    JavaScript 转换器。一个用于所有枚举,一个用于使用枚举的对象。

    public class EnumStringConverter : JavaScriptConverter
    {
        public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
        {
            foreach(string key in dictionary.Keys)
            {
                try { return Enum.Parse(type, dictionary[key].ToString(), false); }
                catch(Exception ex) { throw new SerializationException("Problem trying to deserialize enum from JSON.",ex); }
            }
            return Activator.CreateInstance(type);
        }
    
        public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
        {
            Dictionary<string,object> objs = new Dictionary<string, object>();
            objs.Add(obj.ToString(), ((Enum)obj).ToString("D"));
            return objs;
        }
    
        public override IEnumerable<Type> SupportedTypes{get {return new Type[] {typeof (Enum)};}}
    }
    
    public class SillyConverter : JavaScriptConverter
    {
        public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
        {
            UseSillyEnum se = new UseSillyEnum();
            foreach (string key in dictionary.Keys)
            {
                switch(key)
                {
                    case "PublicEnum":
                        se.PublicEnum = (SomeSillyEnum) Enum.Parse(typeof (SomeSillyEnum), dictionary[key].ToString(), false);
                        break;
                    case "SomeOtherProperty":
                        se.SomeOtherProperty = dictionary[key].ToString();
                        break;
                }
            }
            return se;
        }
    
        public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
        {
            UseSillyEnum se = (UseSillyEnum)obj;
            Dictionary<string, object> objs = new Dictionary<string, object>();
            objs.Add("PublicEnum", se.PublicEnum);
            objs.Add("SomeOtherProperty", se.SomeOtherProperty);
            return objs;
        }
    
        public override IEnumerable<Type> SupportedTypes { get { return new Type[] { typeof(UseSillyEnum) }; } }
    }
    

    并在页面内使用它:

    public partial class _Default : System.Web.UI.Page 
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            /* Handles ALL Enums
    
            JavaScriptSerializer jsonSer = new JavaScriptSerializer(); 
            jsonSer.RegisterConverters( new JavaScriptConverter[] { new EnumStringConverter() } );
    
            string json = jsonSer.Serialize(new UseSillyEnum());
            Response.Write(json);
    
            UseSillyEnum obj = jsonSer.Deserialize<UseSillyEnum>(json);
            Response.Write(obj.PublicEnum);
    
            */
    
            /* Handles Object that uses an enum */
            JavaScriptSerializer jsonSer = new JavaScriptSerializer();
            jsonSer.RegisterConverters( new JavaScriptConverter[] { new SillyConverter() } );
            string json = jsonSer.Serialize(new UseSillyEnum());
            Response.Write(json);
    
            UseSillyEnum obj = jsonSer.Deserialize<UseSillyEnum>(json);
            Response.Write(obj.PublicEnum);
        }
    }
    

    【讨论】:

      【解决方案5】:

      我使用Newtonsoft.Json 库以在WCF 中工作的方式将这个解决方案的所有部分组合在一起。它修复了枚举问题,也使错误处理变得更好,并且可以在 IIS 托管服务中运行。代码挺多的,可以在 GitHub 上找到:https://github.com/jongrant/wcfjsonserializer/blob/master/NewtonsoftJsonFormatter.cs

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-11-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-10-27
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多