【问题标题】:ReadAsAsync deserializing HttpResponseMessage resultReadAsAsync 反序列化 HttpResponseMessage 结果
【发布时间】:2017-12-03 11:22:25
【问题描述】:

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

   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 时,出现如下异常:

错误:System.AggregateException:发生一个或多个错误。 ---> System.AggregateException:发生一个或多个错误。 ---> System.Runtime.Serialization.SerializationException:第 1 行位置 5 出错。需要命名空间“http://schemas.datacontract.org/2004/07/SATA_DTOs”中的元素“ArrayOfPI”。遇到名称为“PI”、命名空间“”的“元素”。 在 System.Runtime.Serialization.DataContractSerializer.InternalReadObject(XmlReaderDelegator xmlReader,布尔值 verifyObjectName,DataContractResolver dataContractResolver) 在 System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator 读取器,布尔值 verifyObjectName,DataContractResolver dataContractResolver) 在 System.Runtime.Serialization.DataContractSerializer.ReadObject(XmlReader 阅读器) 在 System.Net.Http.Formatting.XmlMediaTypeFormatter.ReadFromStream(类型类型,流 readStream,HttpContent 内容,IFormatterLogger formatterLogger) 在 System.Net.Http.Formatting.XmlMediaTypeFormatter.ReadFromStreamAsync(类型类型,流 readStream,HttpContent 内容,IFormatterLogger formatterLogger) --- 从之前抛出异常的位置结束堆栈跟踪 ---

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

状态:200 正常

响应标头:

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();

【问题讨论】:

  • 尝试将 DataContractName 更改为“ArrayOfPI”。此外,请勿使用 .Result 阻止 aysnc 呼叫。如果您不能正确异步,请使用同步 API,而不是 HttpClient。
  • 在这种特殊情况下,您必须读取为字符串,然后加载 xml,检查第一个节点以确定结果是单个对象还是集合,然后根据类型相应地反序列化 xml
  • @Crowcoder 我认为你调用GetAsync(str).ResultGet(str) 没有区别。
  • @Aria,除非这是一个控制台应用程序,否则会有很大的不同。最好的情况是浪费资源,最坏的情况是导致僵局。您应该 await 异步调用或不进行异步调用。
  • @Aria 考虑到响应的动态特性,您需要先检查它,然后再尝试反序列化它。否则你将不得不进行防御性编码并将它们包装在 try/catch 中。

标签: c# .net serialization httpresponse


【解决方案1】:

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

作为最后一次尝试,@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:第 1 行位置 5 出错。需要命名空间“http://schemas.datacontract.org/2004/07/SATA_DTOs”中的元素“ArrayOfPI”。遇到名称为“PI”、命名空间“”的“元素”......

.....

正如提到的例外,它无法反序列化结果我猜输出类型可能是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);
}

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

【讨论】:

    【解决方案2】:

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

    看这一行:

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

    您将结果呈现为 PI,这会导致 &lt;pi&gt; 标记。

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

    System.Runtime.Serialization.SerializationException:第 1 行出错 位置 5. 期望来自命名空间的元素 'ArrayOfPI' 'http://schemas.datacontract.org/2004/07/SATA_DTOs'..遇到 名称为“PI”的“元素”,命名空间为“”

    解决办法是这样的,

    更改您的数据合同您的班级名称:

    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>
    

    这是序列化寻找和期望找到的东西。

    【讨论】:

    • 我有同样的例外Expecting element 'ArrayOfArrayOfPI' from namespace 'http://schemas.datacontract.org/2004/07/SATA_DTOs'.. Encountered 'Element' with name 'PI', namespace ''.
    【解决方案3】:

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

    您拥有IEnumerable&lt;PI&gt;,因此它假定正在读取的内容是基于标准的集合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;
    }
    

    【讨论】:

    • 它也不起作用,不是那个 try 块不起作用。
    猜你喜欢
    • 1970-01-01
    • 2021-02-17
    • 1970-01-01
    • 2015-11-23
    • 1970-01-01
    • 2013-02-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多