Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
394 views
in Technique[技术] by (71.8m points)

asp.net - WebAPI Serialization problems when consuming Recurly Rest API which returns XML

I'm new to the ASP.Net Web API. I'm trying to interact with the Recurly REST based API and I am getting errors like below during my ReadAsAsync call which is the point I believe it attempts to serialize the response.

{"Error in line 1 position 73. Expecting element 'account' from namespace 'http://schemas.datacontract.org/2004/07/RecurlyWebApi.Recurly'.. Encountered 'Element'  with name 'account', namespace ''. "}

Here is my HttpClient implementation, simplified for brevity:

  public class RecurlyClient
  {
    readonly HttpClient client = new HttpClient();

    public RecurlyClient()
    {
      var config = (RecurlySection)ConfigurationManager.GetSection("recurly");

      client.BaseAddress = new Uri(string.Format("https://{0}.recurly.com/v2/", config.Subdomain));

      // Add the authentication header
      client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes(config.ApiKey)));

      // Add an Accept header for XML format.
      client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
    }

    public T Get<T>(string id)
    {
      var accounts = default(T);

      // Make the request and get the response from the service
      HttpResponseMessage response = client.GetAsync(string.Concat("accounts/", id)).Result;  // Blocking call!

      if (response.IsSuccessStatusCode)
      {
        // Parse the response body. Blocking!
        accounts = response.Content.ReadAsAsync<T>().Result;
      }

      return accounts;
    }
  }

And here is my model:

  [XmlRoot("account")]
  public class Account
  {
    [XmlAttribute("href")]
    public string Href { get; set; }

    [XmlElement("account_code")]
    public string AccountCode { get; set; }

    [XmlElement("state")]
    public AccountState State { get; set; }

    [XmlElement("username")]
    public string Username { get; set; }

    [XmlElement("email")]
    public string Email { get; set; }

    [XmlElement("first_name")]
    public string FirstName { get; set; }

    [XmlElement("last_name")]
    public string LastName { get; set; }

    [XmlElement("company_name")]
    public string Company { get; set; }

    [XmlElement("accept_language")]
    public string LanguageCode { get; set; }

    [XmlElement("hosted_login_token")]
    public string HostedLoginToken { get; set; }

    [XmlElement("created_at")]
    public DateTime CreatedDate { get; set; }

    [XmlElement("address")]
    public Address Address { get; set; }
  }

And an example of the XML response from the service:

<account href="https://mysubdomain.recurly.com/v2/accounts/SDTEST01">
  <adjustments href="https://mysubdomain.recurly.com/v2/accounts/SDTEST01/adjustments"/>
  <invoices href="https://mysubdomain.recurly.com/v2/accounts/SDTEST01/invoices"/>
  <subscriptions href="https://mysubdomain.recurly.com/v2/accounts/SDTEST01/subscriptions"/>
  <transactions href="https://mysubdomain.recurly.com/v2/accounts/SDTEST01/transactions"/>
  <account_code>SDTEST01</account_code>
  <state>active</state>
  <username>myusername</username>
  <email>simon@example.co.uk</email>
  <first_name>First name</first_name>
  <last_name>Last name</last_name>
  <company_name>My Company Name</company_name>
  <vat_number nil="nil"></vat_number>
  <address>
    <address1>My Address Line 1/address1>
    <address2>My Address Line 2</address2>
    <city>My City</city>
    <state>My State</state>
    <zip>PL7 1AB</zip>
    <country>GB</country>
    <phone>0123456789</phone>
  </address>
  <accept_language nil="nil"></accept_language>
  <hosted_login_token>***</hosted_login_token>
  <created_at type="datetime">2013-08-22T15:58:17Z</created_at>
</account>
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I think the problem is because by default the DataContractSerializer is being used to deserialize the XML, and by default the DataContractSerializer uses a namespace of namespace http://schemas.datacontract.org/2004/07/Clr.Namespace. (In this case Clr.Namepace is RecurlyWebApi.Recurly.)

Because your XML has attributes, you need to use the XmlSerializer instead of the DataContractSerializer, and you're set up to do this because your account class is decorated with Xml* attributes. However, you have to use an XmlMediaTypeFormatter which is using the XmlSerializer. You can do this by setting a flag on the global XMLFormatter as described on this page:

var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
xml.UseXmlSerializer = true;

or by supplying a MediaTypeFormatter as a parameter to your ReadAsAsync call:

var xmlFormatter = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
xmlFormatter.UseXmlSerializer = true;
accounts = response.ReadAsAsync<T>(xmlFormatter).Result

Not 100% sure of this because this doesn't explain why the first 'account' in your error message is lower case - the DataContractSerializer should ignore the XmlRoot attribute.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...