JavaScriptSerializer -自定义属性名

本文关键字:自定义属性 JavaScriptSerializer | 更新日期: 2023-09-27 18:12:12

我使用JavaScriptSerializer来反序列化json数据。一切都工作得很好,但我的问题是,json数据中的一个属性被命名为'base',所以我不能在我的c#代码中创建这样的属性。我发现我可以手动将值映射到构造函数中的属性,但问题是,我的dto有200个属性,所以我不想手动制作,更愿意找到任何其他解决方案。我也尝试使用注释,但这:

[JsonProperty("base")]
public int baseValue { get; set; }

没有帮助我,value baseValue每次都被设置为0(如果你认为这个注释应该工作,我可以发布我的整个代码,而不仅仅是这两行)

有什么办法可以简单地解决我的问题吗?

JavaScriptSerializer -自定义属性名

分几个部分回答:

  1. 要使属性命名为base,您需要在名称前加上@:

    public int @base { get; set; }
    
  2. 你写你正在使用JavaScriptSerializer。属性[JsonProperty]适用于一个完全不同的序列化器,Json.NET。此属性对JavaScriptSerializer没有影响。

    如果你要切换到Json。. NET中,您将能够使用此属性。

    或者,如果要将数据契约属性应用于类型,则可以使用Json。. NET或DataContractJsonSerializer用重命名的属性序列化类型

  3. 事实上,JavaScriptSerializer没有办法在编写自定义JavaScriptConverter之外重命名序列化属性。这个序列化器非常简单;它唯一支持的序列化属性是ScriptIgnore,用于抑制属性的序列化。

正如dbc所写的,JavaScriptSerializer非常有限。因为我们不能使用Json。在我们的项目中,我编写了一个名为CustomJavaScriptSerializer的JavaScriptConverter来增强JavaScriptSerializer。

不幸的是,由于JavaScriptConverter和JavaScriptSerializer一起工作的方式(这本来可以做得更好,微软!),有必要从CustomJavaScriptSerializer派生要序列化的类。这是唯一的限制。

但是你可以完全控制和灵活地处理你的类是如何序列化/反序列化的。已经内置了一些方便的功能,比如部分支持JsonProperty或将所有属性名的第一个字母小写(这是JavaScript中的惯例)。要了解确切的用法和所有特性,请参阅代码中的注释。除此之外,您还可以在任何派生类中重写序列化方法,以对特定于该特定类的序列化进行微调。

虽然我认为这个班是非常可靠的,当然,像往常一样,我不承担任何责任。使用风险自负。

也就是说,下面是代码:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using System.Web.Script.Serialization;
namespace SomeNamespace
{
    #region Class CustomJavaScriptSerializer
    /// <summary>
    /// Custom JavaScript serializer, see https://msdn.microsoft.com/en-us/library/system.web.script.serialization.javascriptconverter%28v=vs.110%29.aspx
    /// </summary>
    /// <remarks>
    /// Used to enhance functionality of standard <see cref="System.Web.Script.Serialization.JavaScriptSerializer"/>. 
    /// E.g. to support <see cref="JsonPropertyAttribute"/> that provides some properties known from Newtonsoft's JavaScript serializer, 
    /// see https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_JsonPropertyAttribute.htm.
    /// Additionally, there is an attribute <see cref="JsonClassAttribute"/> that can be applied to the class 
    /// to manipulate JSON serialization behavior of all properties of the class.
    /// Use this JSON serializer when including Newtonsoft's JavaScript serializer is not an option for you.
    /// 
    /// Usage: 
    /// 
    ///  - Just derive your class to be JSON serialized / deserialized from this class. 
    ///  - You now can decorate the properties of your class with e.g. [JsonProperty( "someName" )]. See <see cref="JsonPropertyAttribute"/> for details.
    ///  - Additionally, you can decorate your class with e.g. [JsonClass( DoNotLowerCaseFirstLetter = true)]. See <see cref="JsonClassAttribute"/> for details.
    ///  - Important! Use static methods <see cref="JsonSerialize"/> and <see cref="JsonDeserialize"/> of this class for JSON serialization / deserialization.
    ///  - For further customization specific to your class, you can override <see cref="GetSerializedProperty"/> and <see cref="SetDeserializedProperty"/> in your derived class.
    ///   
    /// Example:
    /// 
    /// <![CDATA[
    /// 
    /// [JsonClass( DoNotLowerCaseFirstLetter = true )]
    /// public class SomeClass: CustomJavaScriptSerializer
    /// {
    ///     
    ///     /// <summary>
    ///     /// The tooltip. Will be transferred to JavaScript as "tooltext".
    ///     /// </summary>
    ///     [JsonProperty( "tooltext" )]
    ///     public string Tooltip
    ///     {
    ///         get;
    ///         set;
    ///     }
    ///     
    ///     ...
    /// }
    /// 
    /// ]]>
    /// </remarks>
    public abstract class CustomJavaScriptSerializer : JavaScriptConverter
    {
        #region Fields
        /// <summary>
        /// Dictionary to collect all derived <see cref="CustomJavaScriptSerializer"/> to be registered with <see cref="JavaScriptConverter.RegisterConverters"/>
        /// </summary>
        private static Dictionary<Type, CustomJavaScriptSerializer> convertersToRegister = new Dictionary<Type, CustomJavaScriptSerializer>();
        /// <summary>
        /// Sync for <see cref="convertersToRegister"/>.
        /// </summary>
        private static readonly object sync = new object();
        #endregion
        #region Properties
        /// <summary>
        /// All derived <see cref="CustomJavaScriptSerializer"/> to be registered with <see cref="JavaScriptConverter.RegisterConverters"/>
        /// </summary>
        public static IEnumerable<CustomJavaScriptSerializer> ConvertersToRegister
        {
            get
            {
                return CustomJavaScriptSerializer.convertersToRegister.Values;
            }
        }
        /// <summary>
        /// <see cref="JsonClassAttribute"/> retrieved from decoration of the derived class.
        /// </summary>
        /// <remarks>
        /// Is only set when an instance of this class is used for serialization. See constructor for details.
        /// </remarks>
        protected JsonClassAttribute ClassAttribute
        {
            get;
            private set;
        }
        #endregion
        #region Constructors
        /// <summary>
        /// Default constructor
        /// </summary>
        public CustomJavaScriptSerializer()
        {
            Type type = this.GetType();
            if ( CustomJavaScriptSerializer.convertersToRegister.ContainsKey( type ) )
                return;
            lock( sync )
            {
                // Remember this CustomJavaScriptSerializer instance to do serialization for this type.
                if ( CustomJavaScriptSerializer.convertersToRegister.ContainsKey( type ) )
                    return;
                // Performance: Set ClassAttribute only for the instance used for serialization.
                this.ClassAttribute = ( this.GetType().GetCustomAttributes( typeof( JsonClassAttribute ), true ).FirstOrDefault() as JsonClassAttribute ) ?? new JsonClassAttribute();
                convertersToRegister[ type ] = this;
            }
        }
        #endregion
        #region Public Methods
        /// <summary>
        /// Converts <paramref name="obj"/> to a JSON string.
        /// </summary>
        /// <param name="obj">The object to serialize.</param>
        /// <returns>The serialized JSON string.</returns>
        public static string JsonSerialize( object obj )
        {
            var serializer = new JavaScriptSerializer();
            serializer.RegisterConverters( CustomJavaScriptSerializer.ConvertersToRegister );
            serializer.MaxJsonLength = int.MaxValue;
            return serializer.Serialize( obj );
        }
        /// <summary>
        /// Converts a JSON-formatted string to an object of the specified type.
        /// </summary>
        /// <param name="input">The JSON string to deserialize.</param>
        /// <param name="targetType">The type of the resulting object.</param>
        /// <returns>The deserialized object.</returns>
        public static object JsonDeserialize( string input, Type targetType )
        {
            var serializer = new JavaScriptSerializer();
            serializer.RegisterConverters( CustomJavaScriptSerializer.ConvertersToRegister );
            serializer.MaxJsonLength = int.MaxValue;
            return serializer.Deserialize( input, targetType );
        }
        /// <summary>
        /// Converts the specified JSON string to an object of type <typeparamref name="T"/>.
        /// </summary>
        /// <typeparam name="T">The type of the resulting object.</typeparam>
        /// <param name="input">The JSON string to be deserialized.</param>
        /// <returns>The deserialized object.</returns>
        public static T JsonDeserialize<T>( string input )
        {
            return (T)CustomJavaScriptSerializer.JsonDeserialize( input, typeof( T ) );
        }
        /// <summary>
        /// Get this object serialized as JSON string.
        /// </summary>
        /// <returns>This object as JSON string.</returns>
        public string ToJson()
        {
            return CustomJavaScriptSerializer.JsonSerialize( this );
        }
        #endregion
        #region Overrides
        /// <summary>
        /// Return list of supported types. This is just a derived class here.
        /// </summary>
        [ScriptIgnore]
        public override IEnumerable<Type> SupportedTypes
        {
            get
            {
                return new ReadOnlyCollection<Type>( new List<Type>(){ this.GetType() } );
            }
        }
        /// <summary>
        /// Serialize the passed <paramref name="obj"/>.
        /// </summary>
        /// <param name="obj">The object to serialize.</param>
        /// <param name="serializer">The <see cref="JavaScriptSerializer"/> calling this method.</param>
        /// <returns>A dictionary with name value pairs representing property name value pairs as they shall appear in JSON. </returns>
        public override IDictionary<string, object> Serialize( object obj, JavaScriptSerializer serializer )
        {
            var result = new Dictionary<string, object>();
            if ( obj == null )
                return result;
            BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public;
            PropertyInfo[] properties = this.GetType().GetProperties( bindingFlags );
            foreach ( PropertyInfo property in properties )
            {
                KeyValuePair<string, object> kvp = this.GetSerializedProperty( obj, property );
                if ( !string.IsNullOrEmpty( kvp.Key ) )
                    result[ kvp.Key ] = kvp.Value;
            }
            return result;
        }
        /// <summary>
        /// Deserialize <paramref name="dictionary"/> to <paramref name="type"/>.
        /// </summary>
        /// <remarks>
        /// Reverse method to <see cref="Serialize"/>
        /// </remarks>
        /// <param name="dictionary">The dictionary to be deserialized.</param>
        /// <param name="type">Type to deserialize to. This is the type of the derived class, see <see cref="SupportedTypes"/>.</param>
        /// <param name="serializer">The <see cref="JavaScriptSerializer"/> calling this method.</param>
        /// <returns>An object of type <paramref name="type"/> with property values set from <paramref name="dictionary"/>.</returns>
        public override object Deserialize( IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer )
        {
            if ( dictionary == null )
                throw new ArgumentNullException( "dictionary" );
            if ( type == null )
                throw new ArgumentNullException( "type" );
            if ( serializer == null )
                throw new ArgumentNullException( "serializer" );
            // This will fail if type has no default constructor.
            object result = Activator.CreateInstance( type );
            BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public;
            PropertyInfo[] properties = this.GetType().GetProperties( bindingFlags );
            foreach ( PropertyInfo property in properties )
            {
                this.SetDerializedProperty( result, property, dictionary, serializer );
            }
            return result;
        }
        #endregion
        #region Protected Methods
        /// <summary>
        /// Get a key value pair as base for serialization, based on the passed <paramref name="property"/>.
        /// </summary>
        /// <remarks>
        /// The returned <see cref="KeyValuePair"/> key represents the property's name in JSON, the value its value.
        /// </remarks>
        /// <param name="obj">The object to serialize.</param>
        /// <param name="property">The <see cref="PropertyInfo"/>To be serialized.</param>
        /// <returns>The requested key value pair or an empty key value pair (key = null) to ignore this property.</returns>
        protected virtual KeyValuePair<string, object> GetSerializedProperty( object obj, PropertyInfo property )
        {
            var result = new KeyValuePair<string, object>();
            if ( property == null || !property.CanRead )
                return result;
            object value = property.GetValue( obj );
            if ( value == null && !this.ClassAttribute.SerializeNullValues )
                return result;
            JsonPropertyAttribute jsonPropertyAttribute = this.GetJsonPropertyAttribute( property );
            if ( jsonPropertyAttribute == null || jsonPropertyAttribute.Ignored )
                return result;
            if ( value != null && jsonPropertyAttribute.UseToString )
                value = value.ToString();
            string name = jsonPropertyAttribute.PropertyName;
            return new KeyValuePair<string, object>( name, value );
        }
        /// <summary>
        /// Set <paramref name="property"/> of <paramref name="obj"/> with value provided in <paramref name="dictionary"/>.
        /// </summary>
        /// <param name="obj">The object to set <paramref name="property of"/>.</param>
        /// <param name="property">The property to set its value.</param>
        /// <param name="dictionary">Dictionary with property name - value pairs to query for <paramref name="property"/> value.</param>
        /// <param name="serializer">The <see cref="JavaScriptSerializer"/> calling this method.</param>
        public virtual void SetDerializedProperty( object obj, PropertyInfo property, IDictionary<string, object> dictionary, JavaScriptSerializer serializer )
        {
            if ( obj == null || property == null || !property.CanWrite || dictionary == null || serializer == null )
                return;
            JsonPropertyAttribute jsonPropertyAttribute = this.GetJsonPropertyAttribute( property );
            if ( jsonPropertyAttribute == null || jsonPropertyAttribute.Ignored || jsonPropertyAttribute.UseToString )
                return;
            string name = jsonPropertyAttribute.PropertyName;
            if ( !dictionary.ContainsKey( name ) )
                return;
            object value = dictionary[ name ];
            // Important! Use JavaScriptSerializer.ConvertToType so that CustomJavaScriptSerializers of properties of this class are called recursively. 
            object convertedValue = serializer.ConvertToType( value, property.PropertyType );
            property.SetValue( obj, convertedValue );
        }
        /// <summary>
        /// Gets a <see cref="JsonPropertyAttribute"/> for the passed <see cref="PropertyInfo"/>.
        /// </summary>
        /// <param name="property">The property to examine. May not be null.</param>
        /// <returns>A <see cref="JsonPropertyAttribute"/> with properties set to be used directly as is, never null.</returns>
        protected JsonPropertyAttribute GetJsonPropertyAttribute( PropertyInfo property )
        {
            if ( property == null )
                throw new ArgumentNullException( "property" );
            object[] attributes = property.GetCustomAttributes( true );
            JsonPropertyAttribute jsonPropertyAttribute = null;
            bool ignore = false;
            foreach ( object attribute in attributes )
            {
                if ( attribute is ScriptIgnoreAttribute )
                    ignore = true;
                if ( attribute is JsonPropertyAttribute )
                    jsonPropertyAttribute = (JsonPropertyAttribute)attribute;
            }
            JsonPropertyAttribute result = jsonPropertyAttribute ?? new JsonPropertyAttribute();
            result.Ignored |= ignore;
            if ( string.IsNullOrWhiteSpace( result.PropertyName ) )
                result.PropertyName = property.Name;
            if ( !this.ClassAttribute.DoNotLowerCaseFirstLetter && ( jsonPropertyAttribute == null || string.IsNullOrWhiteSpace( jsonPropertyAttribute.PropertyName ) ) )
            {
                string name = result.PropertyName.Substring( 0, 1 ).ToLowerInvariant();
                if ( result.PropertyName.Length > 1 )
                    name += result.PropertyName.Substring( 1 );
                result.PropertyName = name;
            }
            return result;
        }
        #endregion
    }
    #endregion
    #region Class JsonClassAttribute
    /// <summary>
    /// Attribute to be used in conjunction with <see cref="CustomJavaScriptSerializer"/>.
    /// </summary>
    /// <remarks>
    /// Decorate your class derived from <see cref="CustomJavaScriptSerializer"/> with this attribute to 
    /// manipulate how JSON serialization / deserialization is done for all properties of your derived class.
    /// </remarks>
    [AttributeUsage( AttributeTargets.Class )]
    public class JsonClassAttribute : Attribute
    {
        #region Properties
        /// <summary>
        /// By default, all property names are automatically converted to have their first letter lower case (as it is convention in JavaScript). Set this to true to avoid that behavior.
        /// </summary>
        public bool DoNotLowerCaseFirstLetter
        {
            get;
            set;
        }
        /// <summary>
        /// By default, properties with value null are not serialized. Set this to true to avoid that behavior.
        /// </summary>
        public bool SerializeNullValues
        {
            get;
            set;
        }
        #endregion
    }
    #endregion
    #region Class JsonPropertyAttribute
    /// <summary>
    /// Attribute to be used in conjunction with <see cref="CustomJavaScriptSerializer"/>.
    /// </summary>
    /// <remarks>
    /// Among others, used to define a property's name when being serialized to JSON. 
    /// Implements some functionality found in Newtonsoft's JavaScript serializer, 
    /// see https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_JsonPropertyAttribute.htm
    /// </remarks>
    [AttributeUsage( AttributeTargets.Property )]
    public class JsonPropertyAttribute : Attribute
    {
        #region Properties
        /// <summary>
        /// True to ignore this property.
        /// </summary>
        public bool Ignored
        {
            get;
            set;
        }
        /// <summary>
        /// Gets or sets the name of the property. 
        /// </summary>
        public string PropertyName
        {
            get;
            set;
        }
        /// <summary>
        /// When true, the value of this property is serialized using value.ToString().
        /// </summary>
        /// <remarks>
        /// Can be handy when serializing e.g. enums or types.
        /// Do not set this to true when deserialization is needed, since there is no general inverse method to ToString().
        /// When this is true, the property is just ignored when deserializing.
        /// </remarks>
        public bool UseToString
        {
            get;
            set;
        }
        #endregion
        #region Constructors
        /// <summary>
        /// Default constructor
        /// </summary>
        public JsonPropertyAttribute()
        {
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="JsonPropertyAttribute"/>  class with the specified name. 
        /// </summary>
        /// <param name="propertyName">Name of the property</param>
        public JsonPropertyAttribute( string propertyName )
        {
            this.PropertyName = propertyName;
        }
        #endregion
    }
    #endregion
}

和单元测试:

#region CustomJavaScriptSerializer
/// <summary>Tests behavior of CustomJavaScriptSerializer.</summary>
[TestMethod]
public void TestCustomJavaScriptSerializer()
{
    // 11
    var dataItem11 = new JsonSerializeTest1();
    dataItem11.Label = "LabelName";
    dataItem11.Value = 5;
    dataItem11.Test = TestEnum.B;
    dataItem11.Tooltip = "TooltipName";
    string json11 = dataItem11.ToJson();
    Assert.IsTrue( json11 == "{'"label'":'"LabelName'",'"value'":5,'"test'":2,'"tooltext'":'"TooltipName'"}" );
    JsonSerializeTest1 deserialized11 = CustomJavaScriptSerializer.JsonDeserialize<JsonSerializeTest1>( json11 );
    Assert.IsNotNull( deserialized11 );
    Assert.IsTrue( deserialized11.Equals( dataItem11 ) );
    // 12
    var dataItem12 = new JsonSerializeTest1();
    dataItem12.Value = 5;
    dataItem12.Test = TestEnum.A;
    dataItem12.Tooltip = "TooltipName";
    string json12 = dataItem12.ToJson();
    Assert.IsTrue( json12 == "{'"value'":5,'"test'":1,'"tooltext'":'"TooltipName'"}" );
    JsonSerializeTest1 deserialized12 = CustomJavaScriptSerializer.JsonDeserialize<JsonSerializeTest1>( json12 );
    Assert.IsNotNull( deserialized12 );
    Assert.IsTrue( deserialized12.Equals( dataItem12 ) );
    // 21
    var dataItem21 = new JsonSerializeTest2();
    dataItem21.Label = "LabelName";
    dataItem21.Value = 5;
    dataItem21.Test = TestEnum.B;
    dataItem21.Tooltip = "TooltipName";
    string json21 = dataItem21.ToJson();
    Assert.IsTrue( json21 == "{'"Test'":'"B'",'"Label'":'"LabelName'",'"Value'":5}" );
    JsonSerializeTest2 deserialized21 = CustomJavaScriptSerializer.JsonDeserialize<JsonSerializeTest2>( json21 );
    Assert.IsNotNull( deserialized21 );
    Assert.IsTrue( deserialized21.Label == "LabelName" );
    Assert.IsTrue( deserialized21.Value == 5 );
    // No mistake! UseToString = true here. See JsonPropertyAttribute.UseToString. 
    Assert.IsTrue( deserialized21.Test == 0 );
    Assert.IsTrue( deserialized21.Tooltip == null );
    // 22
    var dataItem22 = new JsonSerializeTest2();
    dataItem22.Tooltip = "TooltipName";
    string json22 = dataItem22.ToJson();
    Assert.IsTrue( json22 == "{'"Test'":'"0'",'"Label'":null,'"Value'":null}" );
    JsonSerializeTest2 deserialized22 = CustomJavaScriptSerializer.JsonDeserialize<JsonSerializeTest2>( json22 );
    Assert.IsNotNull( deserialized22 );
    Assert.IsTrue( deserialized22.Label == null );
    Assert.IsTrue( deserialized22.Value == null );
    Assert.IsTrue( deserialized22.Test == 0 );
    Assert.IsTrue( deserialized22.Tooltip == null );
    var list = new List<JsonSerializeTest1>() { dataItem11, dataItem12 };
    var json = CustomJavaScriptSerializer.JsonSerialize( list );
    List<JsonSerializeTest1> deserialized = CustomJavaScriptSerializer.JsonDeserialize<List<JsonSerializeTest1>>( json );
    Assert.IsNotNull( deserialized );
    Assert.IsTrue( deserialized.Count == 2 );
    Assert.IsTrue( deserialized[ 0 ].Equals( deserialized11 ) );
    Assert.IsTrue( deserialized[ 1 ].Equals( deserialized12 ) );
}

[JsonClass( DoNotLowerCaseFirstLetter = true, SerializeNullValues = true )]
public class JsonSerializeTest2 : JsonSerializeTest1
{
    /// <summary>
    /// A tooltip
    /// </summary>
    [JsonProperty( Ignored = true )]
    public override string Tooltip
    {
        get;
        set;
    }
    /// <summary>
    /// An enum
    /// </summary>
    [JsonProperty( UseToString = true )]
    public override TestEnum Test
    {
        get;
        set;
    }
}
public class JsonSerializeTest1 : CustomJavaScriptSerializer
{
    /// <summary>
    /// A label
    /// </summary>
    public virtual string Label
    {
        get;
        set;
    }
    /// <summary>
    /// A Value
    /// </summary>
    public virtual decimal? Value
    {
        get;
        set;
    }
    /// <summary>
    /// An enum
    /// </summary>
    public virtual TestEnum Test
    {
        get;
        set;
    }
    /// <summary>
    /// A tooltip
    /// </summary>
    [JsonProperty( "tooltext" )]
    public virtual string Tooltip
    {
        get;
        set;
    }
    /// <summary>
    /// Whether this object is the same as <paramref name="obj"/>.
    /// </summary>
    /// <returns>True = <paramref name="obj"/> is the same as this object, false otherwise.</returns>
    public override bool Equals( object obj )
    {
        var other = obj as JsonSerializeTest1;
        // Cast to object to avoid that it calls overridden == operator here.
        if ( (object)other == null )
            return false;
        return this.Label == other.Label && this.Value == other.Value && this.Test == other.Test && this.Tooltip == other.Tooltip;
    }
    /// <summary>
    /// Get hash code for comparison
    /// </summary>
    public override int GetHashCode()
    {
        return base.GetHashCode();
    }
}
public enum TestEnum
{
    A = 1,
    B = 2
}
#endregion

享受吧!