Cơ bản
Trong bài viết này, tôi sử dụng một ứng dụng kiểu Console vì vậy cần
phải thêm tham chiếu đến assembly System.Web.Script.Serialization.
Class JavaScriptSerializer chứa hai phương thức là Serialize() và
Deserialize() dùng để chuyển đối tượng thành chuỗi JSON và ngược lại.
Với một collection, phương thức Serialize() sẽ trả về một chuỗi JSON
dạng mảng.
class Foo { public int Id { get; set; } public string Name { get; set; } public DateTime Birthday { get; set; } } class Program { static void Main(string[] args) { var js = new JavaScriptSerializer(); var foo= new Foo(){ Id = 1, Name = "Bar" , Birthday = DateTime.Today }; var json = js.Serialize(foo); Console.WriteLine( "+ Serialize:\n" +json+ "\n" ); var obj = (Foo) js.Deserialize(json, typeof (Foo)); Console.WriteLine( "+ Deserialize:\nId = {0} \nName = {1} \nBirthday = {2}" ,obj.Id,obj.Name,obj.Birthday); Console.Read(); } } |
+ Serialize: { "Id" :1, "Name" : "Bar" , "Birthday" : "\/Date(1329670800000)\/" } + Deserialize: Id = 1 Name = Bar Birthday = 2/19/2012 5:00:00 PM |
Sử dụng JavaScriptConverter
Trong ví dụ trên, kiểu dữ liệu DateTime được serialize thành một chuỗi
rất khó để đọc và định dạng khi cần thiết. Vì thế tôi sẽ tạo một tạo một
converter sau đó đăng kí cho đối tượng JavaScriptSerializer. Converter
tôi cần tạo phải được thừa kế từ JavaScriptConverter. Class này có 3
thành viên cần được override trong subclass:
- SupportedTypes: property này sẽ trả về một danh sách các kiểu dữ liệu được hỗ trợ bởi converter này.
- Serialize: Phương thức này sẽ tạo một đối tượng Dictionary chứa các cặp name/value dùng để chuyển đổi một đối tượng thành JSON.
- Deserialize: Phương thức này nhận các dữ liệu cần thiết từ một Dictionary để tạo ra đối tượng .NET.
Như vậy Dictionary chính là đối tượng trung gian của quá trình chuyển
đổi giữa đối tượng .NET và JSON. Mọi công việc mà bạn cần thực hiện chỉ
đơn giản là thao tác trên dữ liệu của đối tượng Dictionary.
Tôi sẽ viết một class MyFooConverter và sau đó đăng kí một instance
của nó vào JavaScriptSerializer bằng phương thức RegisterConverters():
public class MyFooConverter : JavaScriptConverter { public override IEnumerable { get { return new List typeof (Foo) }; } } public override IDictionary { var result = new Dictionary var foo = obj as Foo; if (foo == null ) return result; result[ "Id" ] = foo.Id; result[ "Name" ] = foo.Name; result[ "Birthday" ] = foo.Birthday.ToShortDateString(); return result; } public override object Deserialize(IDictionary { if (dictionary == null ) throw new ArgumentNullException( "dictionary" ); return new Foo() { Id = (int) dictionary[ "Id" ], Name = (string) dictionary[ "Name" ], Birthday = DateTime.Parse(dictionary[ "Birthday" ].ToString()) }; } } // Main method: // … var js = new JavaScriptSerializer(); js.RegisterConverters( new []{ new MyFooConverter() }); // ... |
{ "Id" :1, "Name" : "Bar" , "Birthday" : "2/20/2012" } |
Một Converter linh hoạt hơn
Sẽ rất bất tiện nếu như đối tượng bạn cần chuyển đối có quá nhiều
property, việc override hai phương thức Serialize() và Deserialize() sẽ
chiếm một số lượng code kha khá. Vì vậy, để giải quyết vấn đề này, tôi
sẽ sử dụng reflection để tự động xây dựng một Dictionary từ đối tượng:
public class MyFooConverter : JavaScriptConverter { public override IEnumerable { get { return new List typeof (Foo) }; } } public override IDictionary< string , object > Serialize( object obj, JavaScriptSerializer serializer) { if (obj == null ) return new Dictionary< string , object >(); var result = obj.GetType() .GetProperties() .ToDictionary(p => p.Name, p => p.GetValue(obj, null )); result[ "Birthday" ] = ((DateTime)result[ "Birthday" ]).ToShortDateString(); return result; } public override object Deserialize(IDictionary< string , object > dictionary, Type type, JavaScriptSerializer serializer) { if (dictionary == null ) throw new ArgumentNullException( "dictionary" ); return new Foo() { Id = ( int ) dictionary[ "Id" ], Name = ( string ) dictionary[ "Name" ], Birthday = DateTime.Parse(dictionary[ "Birthday" ].ToString()) }; } } |
Đoạn mã trên chỉ là một ví dụ đơn giản về cách làm và nó cần phải cải
tiến để có thể được cho mọi class. Bạn có thể gọi đây là một
JsDateTimeConverter, nhưng dĩ nhiên bạn có thể thay đổi nó để chuyển đối
cả các kiểu dữ liệu khác về định dạng JSON cần thiết. Đây mới thực sự
là đoạn mã tôi muốn giới thiệu:
Một Generic Javascript Converter
Kết hợp generic, LINQ và reflection để giải quyết, tôi được đoạn mã sau:
public class MyJsConverter { public override IEnumerable { get { return new List typeof (T) }; } } public override IDictionary< string , object > Serialize( object obj, JavaScriptSerializer serializer) { if (obj == null ) return new Dictionary< string , object >(); var result = obj.GetType() .GetProperties() .ToDictionary(p => p.Name, p => { var value = p.GetValue(obj, null ); if (value.GetType() == typeof (DateTime)) value = ((DateTime)value).ToShortDateString(); return value; }); return result; } public override object Deserialize(IDictionary< string , object > dictionary, Type type, JavaScriptSerializer serializer) { if (dictionary == null ) throw new ArgumentNullException( "dictionary" ); var foo = Activator.CreateInstance foreach (var property in type.GetProperties()) { var value = dictionary[property.Name]; if (property.PropertyType == typeof (DateTime)) value = DateTime.Parse(value.ToString()); property.SetValue(foo,value, null ); } return foo; } } |
var js = new JavaScriptSerializer(); js.RegisterConverters( new []{ new MyJsConverter()}); |