从具有不同类型的HttpWebResponse GetResponseStream反序列化XML

时间:2014-02-12 23:30:50

标签: .net c#-4.0 xml-serialization httpwebresponse getresponsestream

我正在调用一个Web服务。它接受Http Post。这是简单的XML over Http。您在请求正文中向它发送XML请求文档,您将在响应正文中返回XML响应文档。我在我们的代码库中有一个很好的库,这是我们多年来为请求和响应文档进行强类型序列化而构建的。一切正常。

现在,我正在集成的新服务并不总是在响应中发回相同的对象类型。在某些错误条件下,它返回一个特殊的错误文档。当然,在这些情况下,我的反序列化失败并且响应数据丢失。我知道我有一个反序列化错误,但由于响应丢失,我不知道根本原因是什么。

我认为问题在于GetResponseStream()返回的流不支持搜索操作,因此当我收到错误时,我不能简单地回滚流并重新读取数据并以不同方式处理它。

我正在寻找更好地解决这个问题的策略。

我认为我要做的是将响应流复制到可以寻找的内存流,然后再尝试反序列化。然后,如果发生错误,我可以回滚内存流并以不同方式处理响应数据。 https://stackoverflow.com/a/3434077/90236中有一个很好的例子。

有更好的方法吗?将响应流复制到内存流似乎有点浪费。

原始代码的简化版本:

AccountRequest requestVal = new AccountRequest();
// initialize requestVal object

var request = (HttpWebRequest)WebRequest.Create("http://example.com/service");
request.Method = "POST";
request.ContentType = "text/xml";

using (Stream webStream = request.GetRequestStream())
{
    var serializer = new XmlSerializer(typeof(AccountRequest));
    serializer.Serialize(webStream, requestVal);
    webStream.Flush();
    webStream.Close();
}

AccountResponse returnVal;
using (var response = (HttpWebResponse)request.GetResponse())
{
    Stream responseStream = response.GetResponseStream();

    var serializer = new XmlSerializer(typeof(responseStream));

    try
    {
        returnVal = (AccountResponse)serializer.Deserialize(responseStream);
    }
    catch (Exception ex)
    {
      // if an exception occurs, the response stream data is lost. 
      // The responseStream is not seekable.
      logger.ErrorFormat("After Exception:\n{0}", ex.ToString());
      throw;
    }
}

建议代码的简化版本:

AccountRequest requestVal = new AccountRequest();
// initialize requestVal object

var request = (HttpWebRequest)WebRequest.Create("http://example.com/service");
request.Method = "POST";
request.ContentType = "text/xml";

using (Stream webStream = request.GetRequestStream())
{
    var serializer = new XmlSerializer(typeof(AccountRequest));
    serializer.Serialize(webStream, requestVal);
    webStream.Flush();
    webStream.Close();
}

AccountResponse returnVal;
using (var response = (HttpWebResponse)request.GetResponse())
{
    Stream responseStream = response.GetResponseStream();
    using (MemoryStream ms = new MemoryStream())
    {    
      // copy response stream to a seekable memorystream
      int count = 0;
      do
      {
          byte[] buf = new byte[1024];
          count = responseStream.Read(buf, 0, 1024);
          ms.Write(buf, 0, count);
      } while (responseStream.CanRead && count > 0);    
      ms.Position = 0;

      // now attempt to desrialize from the memory stream
      var serializer = new XmlSerializer(typeof(AccountResponse));

      try
      {
          returnVal = (AccountResponse)serializer.Deserialize(ms);
      }
      catch (Exception ex)
      {
        // if an exception occured, rewind the stream and write an error to the log
        ms.Position = 0;
        using (var reader = new StreamReader(ms, Encoding.UTF8))
        {
            logger.ErrorFormat("After Exception:\n{0}\n\nRespons:\n{1}", 
                  ex.ToString(), reader.ReadToEnd());
        }
        throw;
      }
    }
}

2 个答案:

答案 0 :(得分:2)

如果返回的错误XML具有不同的根元素,则可以在实际反序列化之前使用XmlSerializer.CanDeserialize方法。

答案 1 :(得分:1)

您可以做的是先将整个流作为字符串读取,然后根据需要反序列化/写入日志,而不是复制流。以下是代码段:

 public string RetrieveResponse()
        {

            //Create a new http request
            HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create("http://example.com/service"); ;

            Stream responseStream = null;
            try
            {

                using (var httpResponse = (HttpWebResponse)httpRequest.GetResponse())
                {
                    responseStream = httpResponse.GetResponseStream();

                    if (responseStream == null)
                    {
                        return null;
                    }

                    using (var streamRdr = new StreamReader(responseStream))
                    {
                        var response = streamRdr.ReadToEnd();

                        httpResponse.Close();

                        return response;
                    }
                }

            }
            finally
            {
                if (responseStream != null)
                {
                    responseStream.Dispose();
                }


            }
        }

接下来,您可以使用以下代码段返回的响应反序列化:

private  AccountResponse LoadFromString(string response)
        {
            if (string.IsNullOrEmpty(response))
                return null;


            try
            {
                AccountResponse result = null;
                using (var stringReader = new StringReader(response))
                {
                    using (XmlReader reader = new XmlTextReader(stringReader))
                    {
                        var serializer = new XmlSerializer(typeof(AccountResponse));
                        result = serializer.Deserialize(reader) as AccountResponse;
                    }
                }

                return result;
            }
            catch (Exception exception)
            {
                //Log the exception, etc.

            }

        }

PS:如果您知道服务返回的错误的格​​式,您实际上可以一次性将错误反序列化为对象,因此我们不必尝试捕获。希望这会有所帮助。