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(如果你认为这个注释应该工作,我可以发布我的整个代码,而不仅仅是这两行)
有什么办法可以简单地解决我的问题吗?
分几个部分回答:
-
要使属性命名为
base
,您需要在名称前加上@
:public int @base { get; set; }
-
你写你正在使用
JavaScriptSerializer
。属性[JsonProperty]
适用于一个完全不同的序列化器,Json.NET。此属性对JavaScriptSerializer
没有影响。如果你要切换到Json。. NET中,您将能够使用此属性。
或者,如果要将数据契约属性应用于类型,则可以使用Json。. NET或
DataContractJsonSerializer
用重命名的属性序列化类型 -
事实上,
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
享受吧!