1

クライアント側で WCF を使用して RESTful JSON Web サービスを使用しようとしています。このサービスはサード パーティであるため、サーバーの応答を変更することはできません。

データポイントが 1 つしかない場合、サーバーは次のような応答を返しています...

単一データ ポイント

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

問題は、返されるデータ ポイントが 1 つだけの場合に応答で角かっこが省略されていることだと理解していますが、WCF はその構文を逆シリアル化するとうまく機能しないようです。DataContractJsonSerializer に単一要素配列に角かっこを含めないように指示し、そのシリアライザーを使用するようにサービスに指示する方法はありますか? 多分サービスの振る舞いか何か?

どんな方向でも役に立ちます。

4

2 に答える 2

1

カスタム メッセージ フォーマッタを使用して、JSON の逆シリアル化を必要なデータ コントラクトに変更できます。以下のコードでは、データ コントラクトはList<MyData>;を持つように定義されています。応答にデータポイントが 1 つしか含まれていない場合、デシリアライザーに渡す前にそれを配列に「ラップ」するため、すべてのケースで機能します。

JSON.NET ライブラリを使用して JSON の変更を行っていることに注意してください。ただし、これは必須ではありません (JSON ドキュメントを操作するための便利な JSON DOM があるだけです)。

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();
    }
}
于 2012-10-13T17:01:29.570 に答える
0

WCFの使用について知らないので、Asp.NetWCFに変更します。これがあなたを一つの方法に導く記事です

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);
        }
    }
于 2012-10-10T23:59:49.120 に答える