2012-10-10 60 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