WCF RESTful JSON Web服务客户端中的单个元素数组

时间:2012-10-10 17:32:33

标签: arrays json wcf rest

我正在尝试在客户端使用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允许单个元素数组不包括括号,然后告诉我的服务使用该序列化器?可能是服务行为还是什么?

任何方向都会有所帮助。

2 个答案:

答案 0 :(得分: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();
    }
}

答案 1 :(得分: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);
        }
    }