2010-02-19 29 views
14

Tôi là một chút mới với WCF và sẽ cố gắng mô tả rõ ràng những gì tôi đang cố gắng làm.Generic WCF JSON Deserialization

Tôi có một dịch vụ web WCF sử dụng các yêu cầu JSON. Tôi đang làm tốt gửi/nhận JSON cho hầu hết các phần. Ví dụ, mã sau hoạt động tốt và như mong đợi.

JSON gửi:

{ "guy": {"FirstName":"Dave"} } 

WCF:

[DataContract] 
    public class SomeGuy 
    { 
     [DataMember] 
     public string FirstName { get; set; } 
    } 

    [OperationContract] 
    [WebInvoke(Method = "POST", 
       BodyStyle = WebMessageBodyStyle.WrappedRequest, 
       RequestFormat = WebMessageFormat.Json, 
       ResponseFormat = WebMessageFormat.Json)] 
    public string Register(SomeGuy guy) 
    { 
     return guy.FirstName; 
    } 

này trả về một đối tượng JSON với "Dave" như mong đợi. Vấn đề là tôi không thể luôn đảm bảo rằng JSON tôi nhận được sẽ khớp chính xác với các thành viên trong DataContract của tôi. Ví dụ: JSON:

{ "guy": {"firstname":"Dave"} } 

sẽ không tuần tự chính xác vì trường hợp không khớp. guy.FirstName sẽ rỗng. Hành vi này có ý nghĩa, nhưng tôi không thực sự biết cách để giải quyết vấn đề này. Tôi có phải ép buộc tên trường trên máy khách hoặc có cách nào tôi có thể hòa giải ở phía máy chủ không?

Một câu hỏi có thể có liên quan: Tôi có thể chấp nhận và tuần tự hóa một đối tượng JSON chung vào một StringDictionary hoặc một loại cấu trúc giá trị khóa đơn giản nào đó không? Vì vậy, không có vấn đề gì tên trường được gửi trong JSON, tôi có thể truy cập tên và giá trị đã được gửi cho tôi? Ngay bây giờ, cách duy nhất tôi có thể đọc dữ liệu tôi nhận được là nếu nó khớp chính xác với DataContract được xác định trước.

+1

* Tất nhiên * bạn có thể đảm bảo rằng các json là tuân thủ QTI hợp đồng.Chỉ khi bạn không có quyền kiểm soát của khách hàng, bạn có thể không kiểm soát được nó. Nếu bạn không có quyền kiểm soát của khách hàng thì nó có lẽ nằm ngoài miền của dịch vụ của bạn và điểm là tranh luận. Tôi không thấy vấn đề. Tất cả các cách giải quyết tôi thấy là vô ích và sẽ chỉ thêm đau đớn. Đọc thông số kỹ thuật trên DataContract/DataMember và theo dõi chúng. 2 peso. –

+1

Bạn nói đúng. Về mặt kỹ thuật, tôi có thể viết nó vào JS trước khi thông tin được gửi đi. Và, tôi gợi ý trong một bình luận khác rằng đây có lẽ là những gì tôi sẽ làm cuối cùng. Mặc dù, tôi muốn một dịch vụ có thể xử lý một số trường tùy ý mà không nhất thiết phải biết trước. Điều đó có ý nghĩa? Tôi chỉ muốn một dịch vụ có thể chấp nhận một danh sách hoàn toàn tùy ý của các cặp "key": "value" và sau đó quyết định làm gì với chúng mà không cần phải biết trước tên "key". – davidmdem

Trả lời

11

Dưới đây là một cách khác để đọc json vào từ điển:

[DataContract] 
public class Contract 
    { 
    [DataMember] 
    public JsonDictionary Registration { get; set; } 
    } 

[Serializable] 
public class JsonDictionary : ISerializable 
    { 
    private Dictionary<string, object> m_entries; 

    public JsonDictionary() 
     { 
     m_entries = new Dictionary<string, object>(); 
     } 

    public IEnumerable<KeyValuePair<string, object>> Entries 
     { 
     get { return m_entries; } 
     } 

    protected JsonDictionary(SerializationInfo info, StreamingContext context) 
     { 
     m_entries = new Dictionary<string, object>(); 
     foreach (var entry in info) 
      { 
      m_entries.Add(entry.Name, entry.Value); 
      } 
     } 

    public void GetObjectData(SerializationInfo info, StreamingContext context) 
     { 
     foreach (var entry in m_entries) 
      { 
      info.AddValue(entry.Key, entry.Value); 
      } 
     } 
    } 
+0

+1. Giải pháp tương tự cũng được tìm thấy tại đây: http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/071f73bb-e141-4a68-ae61-05635382934f –

0

Nó thực sự phụ thuộc vào những gì bạn đang sử dụng dữ liệu. Về lý thuyết, bạn có thể giữ nguyên giá trị chung này bằng cách có kiểu trả về chuỗi. Tuy nhiên, dữ liệu sẽ đại diện cho một đối tượng và điều khiển phía máy khách nên biết cách liệt kê đối tượng JSON trả về. Bạn có thể trả về loại cũng có thể hữu ích.

Sau đó, trong mã phía máy khách, bạn có thể có một tính năng biết cách xác định và đọc các loại khác nhau.

3

Bạn có thể thử và thêm DataMember attribute với tên viết thường, nhưng tôi cho rằng bạn muốn có cách tiếp cận phân biệt chữ hoa chữ thường để đối chiếu tên thành viên, trong trường hợp này, sử dụng các thuộc tính DataMember thừa sẽ không hợp lý.

Bạn có thể cung cấp triển khai IDataContractSurrogate, nhưng điều đó sẽ liên quan đến rất nhiều công việc phụ thuộc vào bạn, cũng như rất nhiều phản ánh (không có cách nào để thực hiện điều này theo cách tĩnh có thể được xác minh tại thời gian biên dịch).

Cá nhân, tôi đã từ bỏ việc sử dụng DataContractSerializer class khi nói đến JSON. Thay vào đó, tôi sử dụng Json.NET cho tất cả các nhu cầu JSON của mình. Tôi có thể thấy nơi bạn có thể cắm Json.NET vào DataContractSerializer thông qua một IDataContractSurrogate, nhưng nó vẫn sẽ là một chút thô.

Json.NET là một lựa chọn dễ dàng do khó khăn khi sử dụng DataContractSerializer. Ngoài ra, hãy thêm vào thực tế là DataContractSerializer cho JSON không xử lý chính xác các giá trị DateTimeOffset và điều đó là không có trí tuệ đối với tôi, đặc biệt là vì tôi đang làm việc trong môi trường ASP.NET MVC (cho phép tôi định hình kết quả theo cách tôi muốn). Đây thực sự là lựa chọn tốt hơn nếu bạn đang trưng bày các dịch vụ RESTful bằng cách sử dụng JSON làm mã hóa, bạn có thể trộn và kết hợp với WCF để hiển thị tất cả các điểm cuối trên tất cả các giao thức truyền tải và thông báo bạn cần.

+0

Điều này là thú vị, có bạn bằng cách nào đó kết hợp Json.NET với WCF hoặc bạn đang sử dụng một trình xử lý HTTP tùy chỉnh? – Aaronaught

+0

Tôi muốn sử dụng một trong các thư viện để tự deserialize, nhưng tôi không chắc chắn làm thế nào để ghi đè lên deserialization mặc định xảy ra khi dịch vụ nhận được cuộc gọi. Điều này có thể khớp với bình luận/câu hỏi trên của tôi về cách truy cập JSON thô. – davidmdem

+0

@Aaronaught: Bạn có thể thực hiện theo cả hai cách. DataContractJsonSerializer có nguồn gốc từ XmlObjectSerializer, và bạn có thể làm như vậy, chỉ cần tiêm Json.NET vào cho logic của họ (lưu ý, điều này không phải là tầm thường). Nếu bạn đang sử dụng ASP.NET MVC cho một giao diện RESTful, thì tất cả những gì bạn làm là tạo một ActionResult tùy chỉnh, lấy một đối tượng và trong ghi đè ExecuteResult, bạn sử dụng Json.NET để viết JSON trở lại luồng đầu ra. – casperOne

4

Như tên ngụ ý, dữ liệu hợp đồng là một bộ quy tắc. Nếu bạn muốn ánh xạ tin nhắn một cách đáng tin cậy cho các hoạt động, thì những quy tắc đó cần phải được tuân theo.

Tại sao bạn không thể đảm bảo rằng vỏ bọc sẽ chính xác? Nếu bạn chỉ muốn sử dụng số nhận dạng chữ thường từ JavaScript để thay thế, bạn có thể sử dụng thuộc tính MessageParameter cho điều đó - nhưng bạn vẫn phải chọn một tên cụ thể là. Về lý thuyết, bạn có thể chấp nhận JSON thô và deserialize nó bằng tay (chỉ cần tham số chuỗi và sử dụng bất kỳ thư viện JSON nào để deserialization), nhưng điều đó thực sự không có trong tinh thần WCF.

Tôi nghĩ rằng những gì bạn thực sự cần khắc phục không phải là thực tế là hợp đồng dữ liệu phân biệt chữ hoa chữ thường, nhưng thực tế là JSON không được đặt chính xác ở phía máy khách.


Nếu bạn muốn chấp nhận chuỗi JSON liệu trong hoạt động của bạn, sau đó thay đổi BodyStyle-WebMessageBodyStyle.Bare, và thay đổi phương pháp chữ ký của bạn để chấp nhận một tham số chuỗi duy nhất, mà sẽ được áp dụng với bất cứ chuỗi JSON được gửi bởi khách hàng.

Lưu ý rằng tất cả những gì bạn nhận được là một chuỗi trong số này, bạn phải thực hiện tất cả phân tích cú pháp và bản đồ và xác thực thuộc tính và tự xử lý lỗi. Nó không phải là con đường tôi sẽ chọn để thực hiện, nhưng nếu bạn sẵn sàng chấp nhận nỗ lực và rủi ro liên quan, đó là một lựa chọn tiềm năng.

+0

"những gì bạn thực sự cần sửa là [..] thực tế là JSON không được đặt chính xác ở phía khách hàng." Tôi hoàn toàn đồng ý và, cuối cùng, điều này có thể là những gì tôi sẽ làm. Vấn đề là tôi phải chấp nhận các cuộc gọi từ nhiều biểu mẫu có sẵn mà không sử dụng cấu trúc 'bao gồm' hoặc mẫu, do đó, nó sẽ quay lại và thay đổi tên trường (và một số JS tương ứng) theo cách thủ công. Tôi làm cách nào để truy cập JSON thô? Tôi đã cố gắng nhưng không muốn làm lộn xộn câu hỏi. – davidmdem

+0

@ d12: Xem chỉnh sửa. Có lẽ tôi sẽ không đưa ra lựa chọn này, nhưng nếu bạn làm như vậy, câu trả lời của capserOne có thể giúp với một số chi tiết tốt hơn khi bạn đang cố gắng tìm ra những gì cần làm với JSON thô. – Aaronaught

3

Để đạt được mục tiêu của mình là có một dịch vụ có thể chấp nhận danh sách hoàn toàn các cặp "khóa": "giá trị" dưới dạng chuỗi JSON thô và sau đó quyết định làm gì với chúng mà không phải biết khóa " "tên trước, tôi kết hợp casper và lời khuyên của aaron.

1, để truy cập vào chuỗi JSON liệu, this MSDN blog was very helpful.

tôi đã không thể chỉ đơn giản là thay đổi các tham số phương pháp duy nhất để String và BodyStyle-WebMessageBodyStyle.Bare không có vấn đề. Khi đặt BodyStyle thành Bare, hãy đảm bảo rằng điểm cuối behaviorConfiguration được đặt thành <webHttp/> và không phải là <enableWebScript/>.

Lưu ý thứ hai là, là casperOne mentioned, phương pháp chỉ có thể có 1 tham số. Tham số này cần phải là Stream để truy cập văn bản thô mặc dù (xem blog MSDN ở trên).

Khi bạn có chuỗi JSON thô, nó chỉ là vấn đề deserializing nó thành một StringDictionary. Tôi đã chọn JSON.Net cho điều này và nó hoạt động tuyệt vời. Đây là một ví dụ về xương trần để minh họa.

[OperationContract] 
[WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.Bare, 
    ResponseFormat = WebMessageFormat.Json)] 
public string Register(Stream rawJSON) 
{ 
    // Convert our raw JSON string into a key, value 
    StreamReader sr = new StreamReader(rawJSON); 
    Dictionary<string, string> registration =  
     JsonConvert.DeserializeObject<Dictionary<string, string>>(
      sr.ReadToEnd()); 

    // Loop through the fields we've been sent. 
    foreach (KeyValuePair<string, string> pair in registration) 
    { 
     switch (pair.Key.ToLower()) 
     { 
      case "firstname": 
       return pair.Value; 
       break; 
     } 

    } 
} 

Điều này cho phép tôi chấp nhận danh sách tùy ý các trường thông qua JSON và tên trường không phân biệt chữ hoa chữ thường. Tôi biết đó không phải là cách tiếp cận nghiêm ngặt nhất về tính toàn vẹn dữ liệu hoặc cách các dịch vụ WCF lý tưởng được cấu trúc, nhưng, theo như tôi có thể nói, đó là cách đơn giản nhất để đến nơi tôi muốn đi.

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