ReadAsAsync 反序列化 HttpResponseMessage 结果

ReadAsAsync deserializing HttpResponseMessage result

这个问题可能会重复,但我正在调用如下服务:

   HttpClient httpClinet = new HttpClient();
   httpClinet.DefaultRequestHeaders.Accept.Clear();
   httpClinet.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/xml"));
   var str = "XrayService.asmx/GetOrdData?" + string.Format("ordId={0}&code={1}", range.ordId, range.code);
   HttpResponseMessage response; 
   httpClinet.BaseAddress = new Uri("http://172.16.203.27:6043/"); 
   response = httpClinet.GetAsync(str).Result;
   if (response.IsSuccessStatusCode)
         var caseInfos = response.Content.ReadAsAsync<IEnumerable<PI>>().Result;//Here is exception

一切正常,但是当我想 运行 ReadAsAsync 我得到如下异常:

Error:System.AggregateException: One or more errors occurred. ---> System.AggregateException: One or more errors occurred. ---> System.Runtime.Serialization.SerializationException: Error in line 1 position 5. Expecting element 'ArrayOfPI' from namespace 'http://schemas.datacontract.org/2004/07/SATA_DTOs'.. Encountered 'Element' with name 'PI', namespace ''. at System.Runtime.Serialization.DataContractSerializer.InternalReadObject(XmlReaderDelegator xmlReader, Boolean verifyObjectName, DataContractResolver dataContractResolver) at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator reader, Boolean verifyObjectName, DataContractResolver dataContractResolver) at System.Runtime.Serialization.DataContractSerializer.ReadObject(XmlReader reader) at System.Net.Http.Formatting.XmlMediaTypeFormatter.ReadFromStream(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger) at System.Net.Http.Formatting.XmlMediaTypeFormatter.ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger) --- End of stack trace from previous location where exception was thrown ---

我正在 Google Advanced Rest Client 测试该服务,结果如下:

状态:200 正常

响应Header:

cache-control: private, max-age=0
content-length: 360
content-type: text/xml; charset=utf-8
server:Microsoft-IIS/8.5
x-aspnet-version:4.0.30319
x-powered-by: ASP.NET
date: Sun, 03 Dec 2017 08:37:21 GMT

和输出:

<?xml version="1.0" encoding="utf-8" ?>
<PI>
<ordId>950177248</ordId>
<fnm>بهسا</fnm>
<lnm>حسنی</lnm>
<fthNm>علی</fthNm>
<pId>p2535154</pId>
<sex>F</sex>
<brthD>2003-02-05</brthD>
<addrs />
<nId>0025351540</nId>
<srvNm>|دندان بصورت پانورک</srvNm>
<rfrPhy>مهرزاد اميري-41853</rfrPhy>
 </PI>

我也像这样装饰了 DTO :

namespace SATA_DTOs
{
    [DataContract(Name = "PI")]
    public class PI
    {
        [DataMember] public string ordId { get; set; }
        [DataMember] public string fnm { get; set; }
        [DataMember] public string lnm { get; set; }
        [DataMember] public string fthNm { get; set; }
        [DataMember] public string pId { get; set; }
        [DataMember] public string sex { get; set; }
        [DataMember] public string brthD { get; set; }
        [DataMember] public string addrs { get; set; }
        [DataMember] public string nId { get; set; }
        [DataMember] public string srvNm { get; set; }
        [DataMember] public string rfrPhy { get; set; }
    }
}

更新:

就像另一次尝试一样,我想在这里得到结果 JSONXML 但这也没有区别:

List<PI> model = null;
var client = new HttpClient();
var task = client.GetAsync(httpClinet.BaseAddress.ToString() + str)
      .ContinueWith((taskwithresponse) =>
       {
           var response1 = taskwithresponse.Result;
           var jsonString = response1.Content.ReadAsStringAsync();
                  m_Logging.Log(SharedLib.LoggingMode.Prompt, "JSON string created {0}...", jsonString);
           jsonString.Wait();
           model = Newtonsoft.Json.JsonConvert.DeserializeObject<List<PI>>(jsonString.Result);
        });
task.Wait();

这可能会让人很困惑。这可能看起来很明显,但对某些人来说仍然无法理解。

看看这一行:

var caseInfos = response.Content.ReadAsAsync<IEnumerable<PI>>().Result

您正在将结果呈现为 PI,从而产生 <pi> 标记。

因此,您会得到以下错误:

System.Runtime.Serialization.SerializationException: Error in line 1 position 5. Expecting element 'ArrayOfPI' from namespace 'http://schemas.datacontract.org/2004/07/SATA_DTOs'.. Encountered 'Element' with name 'PI', namespace ''

解决方法是这样的,

更改您的数据合同您的class姓名:

namespace SATA_DTOs
{
    [DataContract(Name = "ArrayOfPI")]
    public class ArrayOfPI
    {
        [DataMember] public string ordId { get; set; }
        [DataMember] public string fnm { get; set; }
        [DataMember] public string lnm { get; set; }
        [DataMember] public string fthNm { get; set; }
        [DataMember] public string pId { get; set; }
        [DataMember] public string sex { get; set; }
        [DataMember] public string brthD { get; set; }
        [DataMember] public string addrs { get; set; }
        [DataMember] public string nId { get; set; }
        [DataMember] public string srvNm { get; set; }
        [DataMember] public string rfrPhy { get; set; }
    }
}

然后像这样赋值this:

 var caseInfos = response.Content.ReadAsAsync<IEnumerable<ArrayOfPI>>().Result

因此,您将收到:

<?xml version="1.0" encoding="utf-8" ?>
<ArrayOfPI>
<ordId>950177248</ordId>
<fnm>بهسا</fnm>
<lnm>حسنی</lnm>
<fthNm>علی</fthNm>
<pId>p2535154</pId>
<sex>F</sex>
<brthD>2003-02-05</brthD>
<addrs />
<nId>0025351540</nId>
<srvNm>|دندان بصورت پانورک</srvNm>
<rfrPhy>مهرزاد اميري-41853</rfrPhy>
 </ArrayOfPI>

序列化查找并期望找到的内容。

当使用 ReadAsAsync 时,框架会尝试使用提供的媒体类型格式化程序解释所需的反序列化类型。

您有 IEnumerable<PI>,因此它假设正在阅读的内容是一个基于标准的集合 ArrayOfPI

在你的例子中,当你告诉它期待一个集合时,一个单一的对象被返回,所以它失败了。

我建议检查集合,如果失败,则考虑到可以返回的响应的动态特性,检查单个对象。

简化示例

public async Task<IEnumerable<PI>> GetDataAsync() {
    var httpClinet = buildClient();
    var str = buildRequestUrl();
    var response = await httpClinet.GetAsync(str);
    IEnumerable<PI> caseInfos = Enumerable.Empty<PI>();
    if (response.IsSuccessStatusCode) {
        try {
            caseInfos = await response.Content.ReadAsAsync<IEnumerable<PI>>();
        } catch {
            //Log?
        }
        if (caseInfos == null) {
            try {
                var singleObject = await response.Content.ReadAsAsync<PI>();
                if (singleObject != null) {
                    caseInfos = new[] { singleObject };
                }
            } catch {
                //Log?
            }
        }
    }
    return caseInfos;
}

在服务器上多次复制粘贴补丁并改进日志我终于解决了问题,

作为 @NKosi 建议的最后一次尝试,稍作改动:

var response1 = httpClinet.GetAsync(str).Result;
IEnumerable<PI> caseInfos1 = Enumerable.Empty<PI>();
try
{
    caseInfos1 = response1.Content.ReadAsAsync<IEnumerable<PI>>().Result;
}
catch (Exception ex)
{
    try
    {
        m_Logging.Log(SharedLib.LoggingMode.Error, "IEnumerable failed, EXP:{0}", ex);
        var singleObject = response1.Content.ReadAsAsync<PI>().Result;
        if (singleObject != null)
        {
            m_Logging.Log(SharedLib.LoggingMode.Error, "singleObject succeeded...");
            caseInfos1 = new[] { singleObject };
        }
    }
    catch (Exception exp)
    {
        m_Logging.Log(SharedLib.LoggingMode.Error, "singleObject failed, EXP:{0}", exp);
    }
}

我也遇到了以下异常:

System.Runtime.Serialization.SerializationException: Error in line 1 position 5. Expecting element 'ArrayOfPI' from namespace 'http://schemas.datacontract.org/2004/07/SATA_DTOs'.. Encountered 'Element' with name 'PI', namespace ''....

.....

正如提到的异常,它无法反序列化结果我猜输出类型可能是 text/html 而不是 text/xml 尽管 Rest Client Tester 将其指定为 text/xml,

出于这个原因,我得出这个结论使用ReadAsStringAsync并将其反序列化为PI,所以通过下面的代码片段我终于得到了结果:

PI caseInfos = null;
try
{
    string strasd = response.Content.ReadAsStringAsync().Result;
    m_Logging.Log(SharedLib.LoggingMode.Prompt, "ReadAsStringAsync() result:{0}", strasd);
    System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(PI));
    using (TextReader reader = new StringReader(strasd))
        caseInfos = (PI)serializer.Deserialize(reader);
    m_Logging.Log(SharedLib.LoggingMode.Prompt, "Deserializing caseInfos model succeeded...");
}
catch (Exception ex)
{
    m_Logging.Log(SharedLib.LoggingMode.Error, "creating model failed, EXP:{0}", ex);
}

感谢所有回答这个问题的人,尤其是那些在本次讨论中分享 his/her 知识的人!!!