2012-04-26 23 views
9

Tôi đang lập kế hoạch xây dựng một WCFservice trả về các đối tượng từ điển chung được tuần tự hóa thành JSON. Thật không may, serialization không thành công, vì đối tượng có khả năng luôn khác nhau. KnownTypes không thể giúp, bởi vì loại thuộc tính là từ điển, và tôi không thể nói KnownType, vì lớp sẽ có khả năng luôn khác nhau.Tôi có thể tuần tự hóa từ điển <string, object> bằng cách sử dụng serializer DataContract không?

Bất kỳ ý tưởng nào nếu có thể tuần tự hóa 'loại không xác định'?

Tôi không nhớ chỉ định DataContract/DataMember cho từng lớp học của tôi, nhưng (ít nhất là cho phiên bản nguyên mẫu), tôi không muốn có loại mạnh cho mỗi và mọi phản hồi. Ứng dụng Javascript chỉ không quan tâm.

Lớp học ẩn danh như thế nào?

+0

Tôi rất vui vì những người như thế này. Xin vui lòng không bận tâm để trả lời với các đối số như thế nào WCF đã không được thực hiện cho rằng vv, vv Tôi sẽ đánh giá cao nếu bạn chỉ có thể cho chúng tôi biết nếu có một cách biết hoặc nếu bạn đã ở đó và đã từ bỏ vì lý do hợp lệ (những gì lý do?). – tishma

Trả lời

7

LƯU Ý: Tôi đã đi vào rất nhiều chi tiết về JavaScriptSerializer ở đầu câu trả lời của tôi, nếu bạn chỉ muốn đọc về độ phân giải cho vấn đề loại đã biết được đề cập trong câu hỏi gốc, nhảy đến cuối của câu trả lời.

Performance

Dựa trên benchmarks Tôi chạy, các JavaScriptSerializer là xa chậm hơn so với các lựa chọn thay thế khác và có thể mất 2x miễn serialize/deserialize một đối tượng so với DataContractSerializer.

Không cần Known Loại

Điều đó nói rằng, các JavascriptSerializer là linh hoạt hơn ở chỗ nó không yêu cầu bạn phải xác định 'loại nổi tiếng' trước thời hạn, và JSON serialized là sạch hơn ít nhất là trong trường hợp từ điển (xem ví dụ here).

Mặt trái của tính linh hoạt đó xung quanh các loại đã biết là sẽ không thể deserialize cùng chuỗi JSON đó trở về kiểu gốc.Ví dụ, giả sử tôi có một Person lớp đơn giản:

public class Person 
{ 
    public string Name { get; set; } 

    public int Age { get; set; } 
} 

Và nếu tôi có thể tạo một thể hiện của Dictinoary<string, object> và thêm một thể hiện của lớp Person với nó trước khi serializing nó:

var dictionary = new Dictionary<string, object>(); 
dictionary.Add("me", new Person { Name = "Yan", Age = 30 }); 
var serializer = new new JavaScriptSerializer(); 
var json = serializer .Serialize(dictionary); 

tôi sẽ nhận JSON sau đây {"me":{"Name":"Yan","Age":30}} rất sạch sẽ nhưng không có bất kỳ thông tin loại nào. Vì vậy, phải nếu bạn có hai lớp với các định nghĩa thành viên cùng hoặc nếu Person được subclassed mà không giới thiệu bất kỳ bổ sung thành viên:

public class Employee : Person 
{ 
} 

sau đó chỉ đơn giản là không có cách nào cho serializer để có thể đảm bảo rằng các JSON {"Name":"Yan","Age":30} có thể được deserialized đúng loại.

Nếu bạn deserialize {"me":{"Name":"Yan","Age":30}} sử dụng JavaScriptSerializer, trong từ điển bạn nhận lại giá trị được liên kết với "tôi" không phải là phiên bản Person mà thay vào đó là Dictionary<string, object>.

Nếu bạn muốn nhận được một trường hợp Person trở lại, bạn có thể (mặc dù bạn có lẽ hầu hết sẽ không bao giờ muốn!) Chuyển đổi mà Dictionary<string, object> sử dụng phương pháp ConvertToType helper:

var clone = serializer.Deserialize<Dictionary<string, object>>(json); 
var personClone = serializer.ConverToType<Person>(clone["me"]); 

Mặt khác, nếu bạn không cần phải lo lắng về việc deserializing những JSON vào đúng loại và JSON serailization không phải là một nút cổ chai hiệu suất (hồ sơ mã của bạn và tìm ra bao nhiêu thời gian CPU dành cho serialization nếu bạn đã không làm như vậy) sau đó tôi muốn nói chỉ cần sử dụng JavaScriptSerializer.

Tiêm Known Loại

NẾU, vào cuối ngày, bạn vẫn cần phải sử dụng DataContractSerializer và cần phải tiêm những KnownTypes, đây là hai điều bạn có thể thử.

1) Chuyển mảng các loại đã biết đến DataContractSerializer constructor.

2) Vượt qua một lớp con của DataContractResolver (với các phương tiện để xác định vị trí các loại quan tâm đến bạn) đến DataContractSerializer constructor

Bạn có thể tạo một 'kiểu registry biết' của các loại mà theo dõi những loại mà có thể được thêm vào từ điển, và nếu bạn kiểm soát tất cả các loại mà bạn sẽ cần phải tiêm vào DataContractSerializer, bạn có thể thử điều đơn giản nhất:

  1. Tạo một lớp KnownTypeRegister với các phương pháp tĩnh để thêm một loại vào danh sách các loại đã biết:

    public static class KnownTypeRegister 
    { 
        private static readonly ConcurrentBag _knownTypes = new ConcurrentBag(); 
        public static void Add(Type type) 
        { 
         _knownTypes.Add(type); 
        } 
        public static IEnumerable Get() 
        { 
         return _knownTypes.ToArray(); 
        } 
    }
  2. Thêm một constructor tĩnh dùng để đăng ký các loại với thanh ghi:

    [DataContract] 
    public class Person 
    { 
        static Person() 
        { 
         KnownTypeRegister.Add(typeof(Person)); 
        } 
        [DataMember] 
        public string Name { get; set; } 
        [DataMember] 
        public int Age { get; set; } 
    }
  3. Lấy mảng của các loại được biết đến từ sổ đăng ký khi bạn xây dựng các serializer:

var serializer = new DataContractSerializer(typeof(Dictionary<string, object>), KnownTypeRegister.Get());

Tùy chọn động/tốt hơn là sở hữu e nhưng chúng cũng khó thực hiện hơn, nếu bạn muốn đọc thêm về độ phân giải kiểu động đã biết, hãy xem bài viết MSDN của Juval Lowy về chủ đề here. Ngoài ra, this blog post bởi Carlos Figueira cũng đi vào chi tiết về các kỹ thuật nâng cao hơn như tạo động các loại, cũng đáng đọc trong khi bạn đang ở trên chủ đề!

+0

Trước hết, cảm ơn phản hồi chi tiết. Nó thực sự có vẻ như nó có giá trị nghiên cứu dọc theo dòng. Tôi cũng đã học về ServiceStack trên blog của bạn, và nó chắc chắn có vẻ đơn giản hơn nhiều khi xây dựng một dịch vụ REST JSON hơn bất cứ thứ gì khác. – tishma

1

từ chối trách nhiệm Tôi không khuyên bạn nên để lộ object trên điểm cuối WCF; altho điều này xuất hiện 'linh hoạt' đây không phải là một ý tưởng tốt vì bạn chưa chỉ định loại thông tin nào sẽ được phục vụ bởi dịch vụ của bạn.

và bây giờ cho một câu trả lời

Nếu cuộc gọi WCF của bạn đang được tiêu thụ bởi một cuộc gọi ajax và khi bạn đặt nó Javascript client just doesn't care. thì tại sao không thực hiện cuộc gọi WCF của bạn chỉ đơn giản là trả về một chuỗi? Sau đó, bên trong của cuộc gọi WCF của bạn có thể sắp đặt từng Dictionary<string, object> sử dụng JavaScriptSerializer

public string MyServiceMethod() 
{ 
    var hash = new Dictionary<string, object>(); 
    hash.Add("key1", "val1"); 
    hash.Add("key2", new { Field1 = "field1", Field2 = 42}); 
    var serialized = new JavaScriptSerializer().Serialize(hash); 
    return serialized; 
} 

disclaimer2 Đây là một phương tiện để kết thúc (kể từ khi bạn hỏi những câu hỏi) - Đối với một ứng dụng chất lượng sản xuất, tôi sẽ có một được xác định rõ giao diện vì vậy nó đã được rõ ràng những gì đã được yêu cầu và gửi lại qua dây.

+0

Có, trả về một chuỗi là điểm hợp lệ. Có để serialize bằng tay trong mỗi phương pháp là không thể chấp nhận mặc dù. Tôi thực sự nghĩ về một số loại wrapper cho điều đó. Tôi chỉ cảm thấy rằng việc sử dụng serialization DataContract sẽ nhanh hơn vì nó là một phần của framework. Là nó? – tishma

+0

Tuy nhiên, mã Javascript sẽ phải biết chính xác những gì được trả lại từ một yêu cầu tất cả các cách từ nguyên mẫu đến sản xuất, và tôi không thể nhìn thấy cách gõ nghiêm ngặt trong. NET có thể giúp với điều đó. Có thể không? – tishma

+1

tishma, tôi không có bất kỳ số liệu nào về việc liệu 'DataContract' có nhanh hơn không. Tuy nhiên nó sẽ không làm tôi ngạc nhiên nếu serializing bằng cách sử dụng một bên thứ ba JSON serializer (ví dụ như Json.NET) và trả về một chuỗi có thể nhanh hơn nhận WCF để serialize. Và không, việc đánh máy nghiêm ngặt sẽ không giúp ích gì với javascript nhưng bạn có thể nhìn vào 'API công khai' và xem chính xác những gì bạn phơi bày như trái ngược với việc hiển thị 'Dictionary '. biết gì. – wal

Các vấn đề liên quan