programing

Data Contract Json Serializer 및 Enums

lastcode 2023. 2. 9. 21:57
반응형

Data Contract Json Serializer 및 Enums

DataContractJsonSerializer를 사용하여 열거 값을 직렬화하면 문자열 이름이 아닌 열거의 숫자 값이 직렬화됩니다.

IE:

enum foo
{
   bar,
   baz
}

foo.bar 값을 시리얼화하면 "bar"가 아닌 "0"이 반환됩니다.

반대로 했으면 좋겠는데, 이걸 무효로 할 수 있는 방법이 있을까요?

편집:

시리얼라이저를 변경하고 싶지 않았기 때문에 간단한 회피책을 사용했습니다.

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

이것은 의도된 것으로 보이며 이 동작은 변경할 수 없습니다.

열거 멤버 값은 JSON에서 숫자로 처리되며, 이는 멤버 이름으로 포함되는 데이터 계약에서 처리되는 방법과 다릅니다.

다음은 원하는 것을 실현하는 대체(및 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());
    }
}

모두가 이 문제를 해결하기 위해 뉴턴소프트의 시리얼라이저를 디폴트로 사용하고 있는 것 같아서 이 문제에 대한 우아한 해결책을 찾느라 정신이 없었습니다.뉴턴소프트는 더 많은 기능을 제공하지만 몇 가지 심각한 결점이 있습니다.몇 가지 예를 들자면 파라미터가 없는 컨스트럭터의 필요성, IENumerable을 구현하는 클래스를 시리얼화하려는 경우 미친 동작, 그리고 추상형이 사용되면(알려진 것을 사용하지 않기 때문에) 매우 나쁜 성능을 발휘합니다.Atribute를 입력하면 회피책에서는 내부 네임스페이스를 발신자에게 공개하는 상세 출력이 생성됩니다).

한편, MVC4 WebApi 솔루션에서 DataContractJsonSerializer를 사용할 때 이를 커스터마이즈하는 방법에 대한 예는 거의 없습니다.

enum을 문자열로 나타내며 DataContractJsonSerializer와 함께 제공되는 기존의 DateTime 포맷 문제를 해결하는 솔루션을 찾는 데 시간이 걸렸습니다.

제1부 - 이 확장 메서드를 확장 클래스에 넣습니다~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

제2부 - 데이터 계약 Json Serializer ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

파트 III - 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;
    }

wcf json에 대한 양방향 직렬화/비실리화를 가져오려면 string 유형의 두 번째 get set 속성을 추가합니다.javascript에서 json 개체를 구성할 때 strong type enum version: 예를 들어 서버 측에서 strongtype enum 버전을 사용합니다.

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
}

편집: 죄송합니다.커피가 없습니다.

다음은 DataContractJsonSerializer가 아닌 Json Serializer로 원하는 작업을 수행하기 위한 코드입니다.

아직 Data Contract Json Serializer로 작업한 적은 없지만 빠르게 문서를 스캔한 결과 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 Converters.모든 enum에 대해 1개, 열거를 사용하는 개체에 대해 1개입니다.

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

이 용액의 을 이 용액을 했습니다.Newtonsoft.JsonWCF 내에서 동작하는 방법으로 라이브러리를 만듭니다.열거형 문제를 수정하고 오류 처리도 훨씬 개선하며 IIS 호스트 서비스에서 작동합니다.꽤 많은 코드이기 때문에 GitHub에서 찾을 수 있습니다.https://github.com/jongrant/wcfjsonserializer/blob/master/NewtonsoftJsonFormatter.cs

하다에는 몇 .Web.config이 기능을 이용하려면 , 다음의 샘플 파일을 참조해 주세요.https://github.com/jongrant/wcfjsonserializer/blob/master/Web.config

언급URL : https://stackoverflow.com/questions/794838/datacontractjsonserializer-and-enums

반응형