如dbc所述,
JavaScriptSerializer非常有限。由于我们不能在项目中使用
Json.NET,因此我编写了一个名为CustomJavaScriptSerializer的
JavaScriptConverter来增强JavaScriptSerializer。
不幸的是,由于JavaScriptConverter和JavaScriptSerializer协同工作的方式(这可以做得更好,Microsoft!),必须从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
public abstract class CustomJavaScriptSerializer : JavaScriptConverter
{
#region Fields
private static Dictionary<Type, CustomJavaScriptSerializer> convertersToRegister = new Dictionary<Type, CustomJavaScriptSerializer>();
private static readonly object sync = new object();
#endregion
#region Properties
public static IEnumerable<CustomJavaScriptSerializer> ConvertersToRegister
{
get
{
return CustomJavaScriptSerializer.convertersToRegister.Values;
}
}
protected JsonClassAttribute ClassAttribute
{
get;
private set;
}
#endregion
#region Constructors
public CustomJavaScriptSerializer()
{
Type type = this.GetType();
if ( CustomJavaScriptSerializer.convertersToRegister.ContainsKey( type ) )
return;
lock( sync )
{
if ( CustomJavaScriptSerializer.convertersToRegister.ContainsKey( type ) )
return;
this.ClassAttribute = ( this.GetType().GetCustomAttributes( typeof( JsonClassAttribute ), true ).FirstOrDefault() as JsonClassAttribute ) ?? new JsonClassAttribute();
convertersToRegister[ type ] = this;
}
}
#endregion
#region Public Methods
public static string JsonSerialize( object obj )
{
var serializer = new JavaScriptSerializer();
serializer.RegisterConverters( CustomJavaScriptSerializer.ConvertersToRegister );
serializer.MaxJsonLength = int.MaxValue;
return serializer.Serialize( obj );
}
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 );
}
public static T JsonDeserialize<T>( string input )
{
return (T)CustomJavaScriptSerializer.JsonDeserialize( input, typeof( T ) );
}
public string ToJson()
{
return CustomJavaScriptSerializer.JsonSerialize( this );
}
#endregion
#region Overrides
[ScriptIgnore]
public override IEnumerable<Type> SupportedTypes
{
get
{
return new ReadOnlyCollection<Type>( new List<Type>(){ this.GetType() } );
}
}
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;
}
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" );
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
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 );
}
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 ];
object convertedValue = serializer.ConvertToType( value, property.PropertyType );
property.SetValue( obj, convertedValue );
}
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
[AttributeUsage( AttributeTargets.Class )]
public class JsonClassAttribute : Attribute
{
#region Properties
public bool DoNotLowerCaseFirstLetter
{
get;
set;
}
public bool SerializeNullValues
{
get;
set;
}
#endregion
}
#endregion
#region Class JsonPropertyAttribute
[AttributeUsage( AttributeTargets.Property )]
public class JsonPropertyAttribute : Attribute
{
#region Properties
public bool Ignored
{
get;
set;
}
public string PropertyName
{
get;
set;
}
public bool UseToString
{
get;
set;
}
#endregion
#region Constructors
public JsonPropertyAttribute()
{
}
public JsonPropertyAttribute( string propertyName )
{
this.PropertyName = propertyName;
}
#endregion
}
#endregion
}
一个单元测试:
#region CustomJavaScriptSerializer
[TestMethod]
public void TestCustomJavaScriptSerializer()
{
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 ) );
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 ) );
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 );
Assert.IsTrue( deserialized21.Test == 0 );
Assert.IsTrue( deserialized21.Tooltip == null );
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
{
[JsonProperty( Ignored = true )]
public override string Tooltip
{
get;
set;
}
[JsonProperty( UseToString = true )]
public override TestEnum Test
{
get;
set;
}
}
public class JsonSerializeTest1 : CustomJavaScriptSerializer
{
public virtual string Label
{
get;
set;
}
public virtual decimal? Value
{
get;
set;
}
public virtual TestEnum Test
{
get;
set;
}
[JsonProperty( "tooltext" )]
public virtual string Tooltip
{
get;
set;
}
public override bool Equals( object obj )
{
var other = obj as JsonSerializeTest1;
if ( (object)other == null )
return false;
return this.Label == other.Label && this.Value == other.Value && this.Test == other.Test && this.Tooltip == other.Tooltip;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
public enum TestEnum
{
A = 1,
B = 2
}
#endregion
享受!
[JsonProperty]
是针对Json.NET的。如果您真的在使用JavaScriptSerializer,那么它就没有任何效果。您确定您没有在使用Json.NET吗? 2) JavaScriptSerializer 没有重命名属性的功能。 3) 要创建一个名为base
的属性,请使用public int @base { get; set; }
。