2010-02-19 48 views
14

我對WCF有點新,並且會試圖清楚地描述我正在嘗試做什麼。通用WCF JSON反序列化

我有一個使用JSON請求WCF web服務。我在大部分時間裏都很好地發送/接收JSON。例如,下面的代碼運行良好,並且如預期的那樣。

JSON發送:

{ "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; 
    } 

這將返回一個JSON對象與預期 「戴夫」。問題是我不能始終保證我收到的JSON將與我的DataContract中的成員完全匹配。例如,JSON:

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

由於大小寫不匹配而無法正確序列化。 guy.FirstName將爲空。這種行爲是有道理的,但我真的不知道如何解決這個問題。我必須強制客戶端上的字段名稱還是有辦法在服務器端協調?

一個可能相關的問題:我可以接受和序列化的通用JSON對象爲StringDictionary或某種簡單的鍵值結構的?因此,無論JSON中發送的字段名稱是什麼,我都可以訪問已發送給我的名稱和值?現在,我可以讀取我收到的數據的唯一方式是它是否與預定義的DataContract完全匹配。

+1

*當然*你可以保證的JSON是符合的合同。只有當你不能控制客戶時,你才能控制它。如果您沒有客戶端的控制權,那麼它可能超出了您的服務範圍,這一點是沒有意義的。我沒有看到問題。我看到的所有解決方法都是徒勞的,只會增加痛苦。閱讀DataContract/DataMember上的規格並遵循它們。 2比索。 –

+1

你說得對。從技術上講,我可以在發送信息之前將它寫入JS。而且,我暗示了另一個評論,這可能是我最終會做的。儘管如此,我希望服務能夠處理任意數量的不必預先知道的字段。那有意義嗎?我只想要一個服務,可以接受完全任意的「密鑰」列表:「值」對,然後決定如何處理它們,而不必事先知道「密鑰」名稱。 – davidmdem

回答

11

這裏讀JSON轉換成字典的另一種方式:

[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。在這裏也可以找到類似的解決方案:http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/071f73bb-e141-4a68-ae61-05635382934f –

0

這實際上取決於你正在使用的數據。從理論上講,你可以通過使用字符串返回類型來保持這種通用性。但是,數據將代表一個對象,並且客戶端控件應該知道如何枚舉返回的JSON對象。您可以返回可能也有幫助的類型。

然後在你的客戶端代碼,你可以有一個知道如何確定和閱讀不同類型的特徵。

3

您可以嘗試使用小寫字母名稱添加DataMember attribute,但我認爲您希望使用不區分大小寫的方式來協調成員名稱,在這種情況下,使用額外的DataMember屬性變得不合理。

可以提供IDataContractSurrogate實現,但這將涉及您的部分很多額外的工作,以及大量反射的(有沒有辦法可以驗證一個靜態的方式來做到這一點在編譯時)。

就個人而言,我已經放棄了使用DataContractSerializer class當談到JSON。相反,我使用Json.NET來滿足我所有的JSON需求。我可以看到你可以通過IDataContractSurrogate將Json.NET插入DataContractSerializer,但它仍然會有點粗糙。

Json.NET是給使用DataContractSerializer的難度一個容易的選擇。另外,請注意JSON的DataContractSerializer並沒有正確處理DateTimeOffset的值,對我來說這是一件容易的事情,特別是因爲我在ASP.NET MVC環境中工作(這允許我調整結果的形式我想要的方式)。

這確實是更好的選擇,如果你正在使用暴露JSON作爲編碼,你可以混合和匹配,與WCF超過你需要的所有傳輸和消息協議揭露所有的端點RESTful服務。

+0

這很有趣,你有沒有把Json.NET和WCF結合起來,還是你使用自定義的HTTP處理程序? – Aaronaught

+0

我想使用其中一個庫手動反序列化,但我不確定如何覆蓋該服務收到該調用時發生的默認反序列化。這可能與我對如何訪問原始JSON的上述評論/問題吻合。 – davidmdem

+0

@Aaronaught:你可以做到這一點。 DataContractJsonSerializer派生自XmlObjectSerializer,你可以做同樣的事情,只是注入Json.NET的邏輯(注意,這不是微不足道的)。如果您將ASP.NET MVC用於RESTful接口,那麼您所做的只是創建一個自定義ActionResult,該自定義ActionResult接受一個對象,並且在ExecuteResult覆蓋中,使用Json.NET將JSON寫回輸出流。 – casperOne

4

顧名思義,數據契約一個是一組規則。如果您想要將消息可靠地映射到操作,那麼需要遵循這些規則。

爲什麼你不能保證套管是正確的?如果你只是想用小寫字母標識符從JavaScript相反,你可以使用MessageParameter屬性爲 - 但你還是要選擇一個特定

在理論上可以接受原始JSON和手動反序列化(只取一個字符串參數和使用任何JSON庫反序列化),但是這真的不是在WCF的精神。

我覺得你真的需要修復是不是事實,數據契約是大小寫敏感的,但事實是,JSON沒有被放在一起正確的客戶端。


如果你希望接受您的操作的原始JSON字符串,然後更改BodyStyleWebMessageBodyStyle.Bare,並改變你的方法簽名接受一個字符串參數,這將與任何JSON字符串被送到填充客戶端。

請注意,只有一個字符串出於此,您必須自己做所有的解析和屬性映射以及驗證和錯誤處理。這不是我選擇採取的路線,但如果你願意承擔所涉及的工作和風險,這是一個潛在的選擇。

+0

「你真正需要解決的是[...]事實上,JSON並沒有在客戶端正確地放在一起。」 我完全同意,最終,這可能是我最終做的。問題是,我必須接受來自許多預先存在的表單的調用,這些表單不使用「包含」或模板結構,所以它會大量返回並手動更改字段名稱(以及一些相應的JS)。 如何訪問原始JSON?我一直在嘗試,但不想混淆這個問題。 – davidmdem

+0

@ d12:請參閱編輯。我可能不會做這個選擇,但是如果你這樣做,capserOne的答案可能會幫助您瞭解一些更精細的細節,一旦您試圖找出如何處理原始JSON。 – Aaronaught

3

爲了實現我的目標,使服務能夠接受完全任意的「key」:「value」對的列表作爲原始JSON字符串,然後決定如何處理它們而不必知道「key 「事先提到名字,我把casper和aaron的建議結合起來。

月1日,訪問原始的JSON字符串,this MSDN blog was very helpful.

我無法簡單地將單一的方法參數更改爲String和BodyStyleWebMessageBodyStyle.Bare沒有問題。將BodyStyle設置爲Bare時,請確保端點behaviorConfiguration設置爲<webHttp/>而不是<enableWebScript/>

第二注意的是,作爲casperOne mentioned,該方法只能有1個參數。該參數需要爲Stream才能訪問原始文本(請參閱上面的MSDN博客)。

一旦你有了原始的JSON字符串,它只是一個反序列化它變成一個StringDictionary的問題。我爲此選擇了JSON.Net,它的功能非常好。這是一個簡單的例子來說明。

[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; 
     } 

    } 
} 

這讓我通過JSON和字段名接受場的任意列表是不區分大小寫。我知道這並不是最嚴格的數據完整性方法,也不是最理想的WCF服務結構,但據我所知,這是實現我想去的最簡單的方法。