JavaScriptSerializer – 自定义属性名称
我正在使用JavaScriptSerializer来反序列化json数据。 一切都很好,但我的问题是,json数据中的一个属性被命名为“base”,所以我不能在我的C#代码中创建这样的属性。 我发现我可以手动将值映射到构造函数中的属性,但问题是,我的DTO有200个属性,所以我不想手动制作,而是希望找到任何其他解决方案。 我也尝试使用注释,但这个:
[JsonProperty("base")] public int baseValue { get; set; }
没有帮助我,值baseValue每次设置为0(如果你认为,这个注释应该工作,我可以发布我的整个代码,不仅这2行)
有什么办法可以简单地解决我的问题吗?
回答几个部分:
-
要创建名为
base
的属性,需要在名称前加上@
:public int @base { get; set; }
-
您写道您正在使用
JavaScriptSerializer
。 属性[JsonProperty]
用于完全不同的序列化程序Json.NET 。 此属性对JavaScriptSerializer
没有影响。如果要切换到Json.NET,则可以使用此属性。
-
事实上,
JavaScriptSerializer
无法在编写自定义JavaScriptConverter
之外重命名属性以进行序列化。 这个序列化器非常简单; 它支持的唯一序列化属性是ScriptIgnore
来抑制属性的序列化。
正如dbc所写, JavaScriptSerializer非常有限。 由于我们不能在项目中使用Json.NET ,因此我编写了一个名为CustomJavaScriptSerializer的JavaScriptConverter来增强JavaScriptSerializer。
不幸的是,由于JavaScriptConverter和JavaScriptSerializer一起工作的方式(本来可以做得更好,微软!),有必要派生你的类从CustomJavaScriptSerializer序列化。 这是唯一的限制。
但是,您可以完全控制和灵活地处理类的序列化/反序列化。 一些方便的function已经很成功,比如对JsonProperty的部分支持或者对所有属性名称的第一个字母进行下限(因为它是JavaScript中的惯例)。 有关确切用法和所有function,请参阅代码中的注释。 除此之外,您还可以覆盖任何派生类中的序列化方法,以微调特定于该特定类的序列化。
虽然我认为课程非常可靠,但一如既往,我不承担任何责任。 使用风险自负。
那就是说,这是代码:
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 /// /// Custom JavaScript serializer, see https://msdn.microsoft.com/en-us/library/system.web.script.serialization.javascriptconverter%28v=vs.110%29.aspx /// /// /// Used to enhance functionality of standard . /// Eg to support 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 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 eg [JsonProperty( "someName" )]. See for details. /// - Additionally, you can decorate your class with eg [JsonClass( DoNotLowerCaseFirstLetter = true)]. See for details. /// - Important! Use static methods and of this class for JSON serialization / deserialization. /// - For further customization specific to your class, you can override and in your derived class. /// /// Example: /// /// /// /// The tooltip. Will be transferred to JavaScript as "tooltext". /// /// /// [JsonProperty( "tooltext" )] /// public string Tooltip /// { /// get; /// set; /// } /// /// ... /// } /// /// ]]> /// public abstract class CustomJavaScriptSerializer : JavaScriptConverter { #region Fields /// /// Dictionary to collect all derived to be registered with /// private static Dictionary convertersToRegister = new Dictionary(); /// /// Sync for . /// private static readonly object sync = new object(); #endregion #region Properties /// /// All derived to be registered with /// public static IEnumerable ConvertersToRegister { get { return CustomJavaScriptSerializer.convertersToRegister.Values; } } /// /// retrieved from decoration of the derived class. /// /// /// Is only set when an instance of this class is used for serialization. See constructor for details. /// protected JsonClassAttribute ClassAttribute { get; private set; } #endregion #region Constructors /// /// Default constructor /// 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 /// /// Converts to a JSON string. /// /// The object to serialize. /// The serialized JSON string. public static string JsonSerialize( object obj ) { var serializer = new JavaScriptSerializer(); serializer.RegisterConverters( CustomJavaScriptSerializer.ConvertersToRegister ); serializer.MaxJsonLength = int.MaxValue; return serializer.Serialize( obj ); } /// /// Converts a JSON-formatted string to an object of the specified type. /// /// The JSON string to deserialize. /// The type of the resulting object. /// The deserialized object. 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 ); } /// /// Converts the specified JSON string to an object of type . /// /// The type of the resulting object. /// The JSON string to be deserialized. /// The deserialized object. public static T JsonDeserialize( string input ) { return (T)CustomJavaScriptSerializer.JsonDeserialize( input, typeof( T ) ); } /// /// Get this object serialized as JSON string. /// /// This object as JSON string. public string ToJson() { return CustomJavaScriptSerializer.JsonSerialize( this ); } #endregion #region Overrides /// /// Return list of supported types. This is just a derived class here. /// [ScriptIgnore] public override IEnumerable SupportedTypes { get { return new ReadOnlyCollection ( new List (){ this.GetType() } ); } } /// /// Serialize the passed . /// /// The object to serialize. /// The calling this method. /// A dictionary with name value pairs representing property name value pairs as they shall appear in JSON. public override IDictionary Serialize( object obj, JavaScriptSerializer serializer ) { var result = new Dictionary(); if ( obj == null ) return result; BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public; PropertyInfo[] properties = this.GetType().GetProperties( bindingFlags ); foreach ( PropertyInfo property in properties ) { KeyValuePair kvp = this.GetSerializedProperty( obj, property ); if ( !string.IsNullOrEmpty( kvp.Key ) ) result[ kvp.Key ] = kvp.Value; } return result; } /// /// Deserialize to . /// /// /// Reverse method to /// /// The dictionary to be deserialized. /// Type to deserialize to. This is the type of the derived class, see . /// The calling this method. /// An object of type with property values set from . public override object Deserialize( IDictionary 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 /// /// Get a key value pair as base for serialization, based on the passed . /// /// /// The returned key represents the property's name in JSON, the value its value. /// /// The object to serialize. /// The To be serialized. /// The requested key value pair or an empty key value pair (key = null) to ignore this property. protected virtual KeyValuePair GetSerializedProperty( object obj, PropertyInfo property ) { var result = new KeyValuePair(); 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( name, value ); } /// /// Set of with value provided in . /// /// The object to set . /// The property to set its value. /// Dictionary with property name - value pairs to query for value. /// The calling this method. public virtual void SetDerializedProperty( object obj, PropertyInfo property, IDictionary 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 ); } /// /// Gets a for the passed . /// /// The property to examine. May not be null. /// A with properties set to be used directly as is, never null. 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 /// /// Attribute to be used in conjunction with . /// /// /// Decorate your class derived from with this attribute to /// manipulate how JSON serialization / deserialization is done for all properties of your derived class. /// [AttributeUsage( AttributeTargets.Class )] public class JsonClassAttribute : Attribute { #region Properties /// /// 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. /// public bool DoNotLowerCaseFirstLetter { get; set; } /// /// By default, properties with value null are not serialized. Set this to true to avoid that behavior. /// public bool SerializeNullValues { get; set; } #endregion } #endregion #region Class JsonPropertyAttribute /// /// Attribute to be used in conjunction with . /// /// /// 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 /// [AttributeUsage( AttributeTargets.Property )] public class JsonPropertyAttribute : Attribute { #region Properties /// /// True to ignore this property. /// public bool Ignored { get; set; } /// /// Gets or sets the name of the property. /// public string PropertyName { get; set; } /// /// When true, the value of this property is serialized using value.ToString(). /// /// /// Can be handy when serializing eg 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. /// public bool UseToString { get; set; } #endregion #region Constructors /// /// Default constructor /// public JsonPropertyAttribute() { } /// /// Initializes a new instance of the class with the specified name. /// /// Name of the property public JsonPropertyAttribute( string propertyName ) { this.PropertyName = propertyName; } #endregion } #endregion }
并进行unit testing:
#region CustomJavaScriptSerializer /// Tests behavior of CustomJavaScriptSerializer. [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( 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 ( 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( 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 ( 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() { dataItem11, dataItem12 }; var json = CustomJavaScriptSerializer.JsonSerialize( list ); List deserialized = CustomJavaScriptSerializer.JsonDeserialize>( 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 { /// /// A tooltip /// [JsonProperty( Ignored = true )] public override string Tooltip { get; set; } /// /// An enum /// [JsonProperty( UseToString = true )] public override TestEnum Test { get; set; } } public class JsonSerializeTest1 : CustomJavaScriptSerializer { /// /// A label /// public virtual string Label { get; set; } /// /// A Value /// public virtual decimal? Value { get; set; } /// /// An enum /// public virtual TestEnum Test { get; set; } /// /// A tooltip /// [JsonProperty( "tooltext" )] public virtual string Tooltip { get; set; } /// /// Whether this object is the same as . /// /// True = is the same as this object, false otherwise. 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; } /// /// Get hash code for comparison /// public override int GetHashCode() { return base.GetHashCode(); } } public enum TestEnum { A = 1, B = 2 } #endregion
请享用!