2012-10-10 32 views
1

我試圖在客戶端使用WCF使用RESTful JSON Web服務。該服務是第三方,所以我無法對服務器響應進行任何更改。WCF中的單元素數組RESTful JSON Web服務客戶端

服務器發回看起來像這樣的時候,只有一個數據點的響應...

單個數據點

{ 
    "Data": 
    { 
    "MyPropertyA":"Value1", 
    "MyPropertyB":"Value2" 
    }, 
} 

和這樣的事情時有不止一個數據點...

多個數據點

{ 
    "Data": 
    [ 
    { 
     "MyPropertyA":"Value1", 
     "MyPropertyB":"Value2" 
    }, 
    { 
     "MyPropertyA":"Value3", 
     "MyPropertyB":"Value4" 
    }, 
    { 
     "MyPropertyA":"Value5", 
     "MyPropertyB":"Value6" 
    } 
    ], 
} 

我有我的服務合同成立這樣的...

[ServiceContract] 
public interface IRewardStreamService 
{ 
    [OperationContract] 
    [WebInvoke] 
    MyResponse GetMyStuff(); 
} 

和這樣的數據點的數據合約...

[DataContract] 
public class MyData 
{ 
    [DataMember] 
    public string MyPropertyA { get; set; } 

    [DataMember] 
    public string MyPropertyB { get; set; } 
} 

,我能得到的唯一途徑單數據點響應的工作是如果我有這樣一個單一的實例屬性,但這不會解析多個數據點響應...

響應唱歌樂實例

[DataContract] 
public class MyResponse 
{ 
    [DataMember] 
    public MyData Data { get; set; } 
} 

,我可以得到多個數據點響應工作的唯一辦法是,如果我有一個數組/列表實例的屬性就是這樣,但這並不解析單個數據點的響應...對於多實例

響應

[DataContract] 
public class MyResponse 
{ 
    [DataMember] 
    public IList<MyData> Data { get; set; } 
} 

我理解的問題是,響應被省略了支架有當只有一個數據點返回,但似乎WCF做不能很好地反序列化該語法。有什麼方法可以告訴DataContractJsonSerializer允許單個元素數組不包含括號,然後告訴我的服務使用該序列化器?也許服務行爲或什麼?

任何方向都會有所幫助。

+0

你可以嘗試使用Javascript反序列化器而不是Datacontractjsonserializer。另請參考以下鏈接:http://stackoverflow.com/questions/596271/deserialization-problem-with-datacontractjsonserializer – Rajesh

+0

您也可以通過這個好文章,關於你如何使用序列化反序列化JSON字符串解釋說:HTTP ://www.codeproject.com/Articles/272335/JSON-Serialization-and-Deserialization-in-ASP-NET – Rajesh

+0

@Rajesh,你知道的一種方式來注入的JavaScript解串器到WCF管道到位DataContractJsonSerializer的? – TylerOhlsen

回答

1

您可以使用自定義消息格式化程序將JSON的反序列化更改爲所需的數據協定。在下面的代碼中,數據合同定義爲List<MyData>;如果響應僅包含一個數據點,它將在傳遞給解串器之前將其「包裝」到數組中,因此它將適用於所有情況。我使用了JSON.NET庫做的JSON修改,但是這不是一個要求

通知(它只是有一個漂亮的JSON DOM與JSON文件工作)。

public class StackOverflow_12825062 
{ 
    [ServiceContract] 
    public class Service 
    { 
     [WebGet] 
     public Stream GetData(bool singleDataPoint) 
     { 
      string result; 
      if (singleDataPoint) 
      { 
       result = @"{ 
        ""Data"": 
        { 
        ""MyPropertyA"":""Value1"", 
        ""MyPropertyB"":""Value2"" 
        }, 
       }"; 
      } 
      else 
      { 
       result = @"{ 
        ""Data"": 
        [ 
        { 
         ""MyPropertyA"":""Value1"", 
         ""MyPropertyB"":""Value2"" 
        }, 
        { 
         ""MyPropertyA"":""Value3"", 
         ""MyPropertyB"":""Value4"" 
        }, 
        { 
         ""MyPropertyA"":""Value5"", 
         ""MyPropertyB"":""Value6"" 
        } 
        ], 
       } "; 
      } 

      WebOperationContext.Current.OutgoingResponse.ContentType = "application/json"; 
      return new MemoryStream(Encoding.UTF8.GetBytes(result)); 
     } 
    } 
    [DataContract] 
    public class MyData 
    { 
     [DataMember] 
     public string MyPropertyA { get; set; } 

     [DataMember] 
     public string MyPropertyB { get; set; } 
    } 
    [DataContract] 
    public class MyResponse 
    { 
     [DataMember] 
     public List<MyData> Data { get; set; } 

     public override string ToString() 
     { 
      return string.Format("MyResponse, Data.Length={0}", Data.Count); 
     } 
    } 
    [ServiceContract] 
    public interface ITest 
    { 
     [WebGet] 
     MyResponse GetData(bool singleDataPoint); 
    } 
    public class MyResponseSingleOrMultipleClientReplyFormatter : IClientMessageFormatter 
    { 
     IClientMessageFormatter original; 
     public MyResponseSingleOrMultipleClientReplyFormatter(IClientMessageFormatter original) 
     { 
      this.original = original; 
     } 

     public object DeserializeReply(Message message, object[] parameters) 
     { 
      WebBodyFormatMessageProperty messageFormat = (WebBodyFormatMessageProperty)message.Properties[WebBodyFormatMessageProperty.Name]; 
      if (messageFormat.Format == WebContentFormat.Json) 
      { 
       MemoryStream ms = new MemoryStream(); 
       XmlDictionaryWriter jsonWriter = JsonReaderWriterFactory.CreateJsonWriter(ms); 
       message.WriteMessage(jsonWriter); 
       jsonWriter.Flush(); 
       string json = Encoding.UTF8.GetString(ms.ToArray()); 
       JObject root = JObject.Parse(json); 
       JToken data = root["Data"]; 
       if (data != null) 
       { 
        if (data.Type == JTokenType.Object) 
        { 
         // single case, let's wrap it in an array 
         root["Data"] = new JArray(data); 
        } 
       } 

       // Now we need to recreate the message 
       ms = new MemoryStream(Encoding.UTF8.GetBytes(root.ToString(Newtonsoft.Json.Formatting.None))); 
       XmlDictionaryReader jsonReader = JsonReaderWriterFactory.CreateJsonReader(ms, XmlDictionaryReaderQuotas.Max); 
       Message newMessage = Message.CreateMessage(MessageVersion.None, null, jsonReader); 
       newMessage.Headers.CopyHeadersFrom(message); 
       newMessage.Properties.CopyProperties(message.Properties); 
       message = newMessage; 
      } 

      return this.original.DeserializeReply(message, parameters); 
     } 

     public Message SerializeRequest(MessageVersion messageVersion, object[] parameters) 
     { 
      throw new NotSupportedException("This formatter only supports deserializing reply messages"); 
     } 
    } 
    public class MyWebHttpBehavior : WebHttpBehavior 
    { 
     protected override IClientMessageFormatter GetReplyClientFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint) 
     { 
      IClientMessageFormatter result = base.GetReplyClientFormatter(operationDescription, endpoint); 
      if (operationDescription.Messages[1].Body.ReturnValue.Type == typeof(MyResponse)) 
      { 
       return new MyResponseSingleOrMultipleClientReplyFormatter(result); 
      } 
      else 
      { 
       return result; 
      } 
     } 
    } 
    public static void Test() 
    { 
     string baseAddress = "http://" + Environment.MachineName + ":8000/Service"; 
     WebServiceHost host = new WebServiceHost(typeof(Service), new Uri(baseAddress)); 
     host.Open(); 
     Console.WriteLine("Host opened"); 

     ChannelFactory<ITest> factory = new ChannelFactory<ITest>(new WebHttpBinding(), new EndpointAddress(baseAddress)); 
     factory.Endpoint.Behaviors.Add(new MyWebHttpBehavior()); 
     ITest proxy = factory.CreateChannel(); 

     Console.WriteLine(proxy.GetData(false)); 
     Console.WriteLine(proxy.GetData(true)); 

     Console.Write("Press ENTER to close the host"); 
     ((IClientChannel)proxy).Close(); 
     factory.Close(); 
     Console.ReadLine(); 
     host.Close(); 
    } 
} 
+0

正是我尋找的解決方案。非常感謝發佈這樣一個優秀和易於閱讀/理解答案! – TylerOhlsen

0

我不知道使用WCF,所以我會更改爲Asp.Net WCF。下面是一篇文章,將讓你一個我只是無法弄清楚如何確定它是否是一個數組或單個對象的方式

http://www.west-wind.com/weblog/posts/2012/Aug/30/Using-JSONNET-for-dynamic-JSON-parsing

。這是一個小代碼。

[TestMethod] 
    public void SingleObject() 
    { 
     using (var client = new HttpClient()) 
     { 
      var result = client.GetStringAsync("http://localhost:8080/api/JSONTestOne"); 
      string content = result.Result; 
      JObject jsonVal = JObject.Parse(content); 
      dynamic aFooObj = jsonVal; 
      Console.WriteLine(aFooObj.afoo.A); 
     } 
    } 

    [TestMethod] 
    public void ArrayWithObject() 
    { 
     using (var client = new HttpClient()) 
     { 
      var result = client.GetStringAsync("http://localhost:8080/api/JSONTest"); 
      string content = result.Result; 
      JObject jsonVal = JObject.Parse(content); 
      dynamic foos = jsonVal; 
      Console.WriteLine(foos[0].A); 
     } 
    } 
+0

我真的想堅持使用WCF。此外,我寧願不添加另一個外部依賴項到我的項目。 – TylerOhlsen

+0

在這種情況下。我會提出請求並使用WebRequest對象獲取字符串。然後解析它以確定使用什麼合約來反序列化它。可能有一種方法可以將它引入WCF管道,但我不知道如何。 – suing