1
votes

This queston may possible duplicated but I am calling a service as the following below :

   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

everything is going fine, but when I want to run ReadAsAsync I got the exception as below:

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 ---

I am testing that service by Google Advanced Rest Client and see the result as :

Status : 200 OK

Response 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

and OutPut :

<?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>

I also decorated DTO like :

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

enter image description here

UPDATE:

Just as another try I want to get the result here either JSON or XML but this is also does not make difference:

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();
3
Try changing DataContractName to "ArrayOfPI". Also, do not block on aysnc calls with .Result. If you can't do proper async then use a synchronous API, not HttpClient. - Crowcoder
In this particular case, you will have to read as string then load the xml, inspect the first node to determine if result is single object or collection and then deseiralize the xml accordingly based on type - Nkosi
@Crowcoder I think there is no difference either you call GetAsync(str).Result or Get(str) . - Aria
@Aria, there is a big difference unless this is a console app. At best it is a waste of resources, at worst you cause a deadlock. You should await async calls or not make async calls. - Crowcoder
@Aria given the dynamic nature of the response you will need to inspect it first before trying to deserialize it. otherwise you will have to code defensively and wrap them in try/catch for either case. - Nkosi

3 Answers

1
votes

After many copy pasting patch on server and improving the log I finally resolve the problem,

As the last try which @NKosi suggested with little changes:

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

I crossed with the below exception also:

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 ''....

.....

as the exception mentioned it can't be able to deserialize the result I guessed the out put type may is text/html not text/xml although Rest Client Tester specified it as text/xml,

for this reason I came to this conclusion to use ReadAsStringAsync and deserialize it to PI, so by the below snipped code I finally get the result:

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

I appreciate all crossed this question especially those who shared his/her knowledge in this discussion!!!

0
votes

This is can be quite confusing. This might seem obvious the eye but still, not understandable to some.

Look at this line:

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

You are rendering the result as PI, which results in <pi> tag.

Thus, you get the following error:

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 ''

The solution is this,

change your data contract and your class name:

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

Then assign it like this:

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

Thus, you will receive this:

<?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>

Which is what the serialization looks for and expects to find.

0
votes

When using ReadAsAsync the framework tries to interpret the desired type for deserialization using the provided media type formatter.

You have IEnumerable<PI> so it assumes that the content being read is a collection ArrayOfPI based on standards.

In your example a single object is being returned while you tell it to expect a collection so it fails.

I suggest checking for collection and if that fails then check for single object given the dynamic nature of the responses that can be returned.

Simplified example

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