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>
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.
Related
I have deployed a REST API in IIS, in which a GET method returns an Arraylist of Student Class. How can I consume XML root Element "ArrayOfStudent" in c# using HttpClient? Below is the code I have written so far.
API get method
[HttpGet]
[ResponseType(typeof(IEnumerable<Student>))]
public IHttpActionResult Get()
{
using (handler) //handler is just EF code to get data
{
return Ok(handler.Get());
}
}
API XML response
<ArrayOfStudent xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Com.CompanyName.Component.Entity">
<Student>
<Id>1</Id>
<Name>John</Name>
</Student>
</ArrayOfStudent>
Http Client Code
static void Main(string[] args)
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:55587/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
Task<HttpResponseMessage> responseTask = client.GetAsync("api/Student");
responseTask.Wait();
///////Error is on this line of code ////////
var ListTask = responseTask.Content.ReadAsAsync<IEnumerable<Student>>();
ListTask.Wait();
IEnumerable<Student> list = ListTask.Result;
return list;
}
Inner Exception
Inner Exception 1:
SerializationException: Error in line 1 position 150. Expecting element 'ArrayOfStudent' from namespace 'http://schemas.datacontract.org/2004/07/Com.CompanyName.ApiAgent.Entity'.. Encountered 'Element' with name 'ArrayOfStudent', namespace 'http://schemas.datacontract.org/2004/07/Com.CompanyName.Component.Entity'.
Student class -- Simple
using System;
namespace Com.CompanyName.Entity
{
public class Student
{
public long Id { get; set; }
public string Name { get; set; }
}
}
Your xml is being serialized by the server with the DataContractSerializer. You can tell, because the namespace in your xml is http://schemas.datacontract.org/2004/07/Com.CompanyName. By default, the DataContractSerializer creates an xml namespace of http://schemas.datacontract.org/2004/07/{namespace}, where {namespace} is the C# namespace your class is defined in.
So on the server side, your student class is defined like so:
namespace Com.CompanyName
{
public class Student
{
public long Id { get; set; }
public string Name { get; set; }
}
}
Notice the difference in namespace. Your Student class is defined in namespace Com.CompanyName.Entity, which is why the DataContractSerializer has difficulty understanding the returned xml. Simply changing the namespace for your Student class to Com.CompanyName would solve your problem.
However, this is pretty annoying, because that would mean you'd have to define all those classes in the same namespace as the server, and that would couple client and server very tightly. Fortunately, you can define your xml namespaces, and that is what I'd recommend you to do. Always use explicit namespace for your xml data contracts, to make the interchange more robust for future internal changes.
On both the server and client side, define your Student class like this:
[DataContract(Namespace = "http://companyname.com/schemas/student")]
public class Student
{
[DataMember]
public long Id { get; set; }
[DataMember]
public string Name { get; set; }
}
Now, we've defined a new namespace, and the xml would be serialized with the specified namespace. Because of this, it does not matter in which C# namespace you've defined your class anymore. Your xml would become something like this:
<ArrayOfStudent xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://companyname.com/schemas/student">
<Student>
<Id>1</Id>
<Name>John</Name>
</Student>
</ArrayOfStudent>
You could also generate your C# classes from your xml. There are several tools available for this.
I apologize for the irrelevance earlier I needed to see the student class before I give you the answer, you must change your student class or create new ones as models and pass them to your entity, you need to include the following two classes in order to serialize your XML to your desired object / list
[XmlRoot("ArrayOfStudent")]
public class ArrayOfStudent
{
[XmlElement("Student")]
public IEnumerable<Student> Students { get; set; }
}
public class Student
{
[XmlElement("Id")]
public int Id { get; set; }
[XmlElement("Name")]
public string Name { get; set; }
}
The Code you need to serialize from xml to object:
HttpResponseMessage response = client.GetAsync("api/Student");
var readAsStringAsync = response.Content.ReadAsStringAsync();
string result = readAsStringAsync.Result;
XmlSerializer serializer = new XmlSerializer(typeof(ArrayOfStudent));
using (TextReader reader = new StringReader(result))
{
ArrayOfStudent result = (ArrayOfStudent) serializer.Deserialize(reader);
}
The below is just to improve your design it is not necessary.
It is always recommended to use generic code for this kind of requests as the below class it saves you alot of time and u can use it almost anywhere.
public class ApiResult<T>
{
public IEnumerable<T> List { get; set; }
public T Object { get; set; }
public string Message { get; set; }
public bool Success { get; set; }
public HttpStatusCode StatusCode { get; set; }
public string Url { get; set; }
public static ApiResult<T> Post(string uri, string url, string token = null)
{
using (var client = new HttpClient())
{
client.Timeout = TimeSpan.FromMilliseconds(1800000);
client.BaseAddress = new Uri(uri);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
if (!string.IsNullOrEmpty(token))
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", token);
}
HttpResponseMessage response = client.PostAsync(url, null).Result;
return Result(response);
}
}
public static ApiResult<T> Get(string uri, string url, string token = null)
{
using (var client = new HttpClient())
{
client.Timeout = TimeSpan.FromMilliseconds(1800000);
client.BaseAddress = new Uri(uri);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
if (!string.IsNullOrEmpty(token))
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", token);
}
HttpResponseMessage response = client.GetAsync(url).Result;
return Result(response);
}
}
private static ApiResult<T> Result(HttpResponseMessage response)
{
ApiResult<T> result = response.Content.ReadAsAsync<ApiResult<T>>().Result;
if (response.StatusCode == HttpStatusCode.Unauthorized || response.StatusCode == HttpStatusCode.Forbidden)
{
result.StatusCode = response.StatusCode;
}
if (response.IsSuccessStatusCode)
{
result.Success = true;
}
return result;
}
}
now you can write your main method this way if you implement the above class:
static void Main(string[] args)
{
// both Uri & Url u can get it from ur app.config or web.config to make it easier to edit on publish saves you alot of time.
string Uri = "http://localhost:55587";
string Url = "api/Student";
string token = "yourtoken"; //(optional)
ApiResult<Student> result = ApiResult<Student>.Get(Uri, "/" + Url, token);
if(result.Success && result.List.Any())
{
IEnumerable<Student> list = result.List;
return list;
}
else
//return error if result.success is false else there are no records...
}
[Update: This question is different from the suggested duplicate because this one is about deserialization of XML and the explanation of the problem and solution on this one is clearer as I've included the full source code.]
I'm trying to read and subsequently manipulate a response from a Web API. Its response looks like this:
<MYAPI xsi:noNamespaceSchemaLocation="MYAPI.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<MySite Resource="some resource name">
<Name>some name</Name>
<URL>some url</URL>
<SecondName>Describes something</SecondName>
</MySite>
... A lot of these <MySite>...</MySite> are there
<SomeOtherSite Resource="some resource name">
<Name>some name</Name>
<URL>some url</URL>
</SomeOtherSite>
</MYAPI>
SomeOtherSite is not repeating and only one of it appears at the end of the response. But the MySite is the one that is repeating.
I've modeled the class for this XML response as:
public class MYAPI
{
public List<MySite> MySite { get; set; }
public SomeOtherSite SomeOtherSite { get; set; }
}
public class MySite
{
public string Name { get; set; }
public string URL { get; set; }
public string SecondName { get; set; }
}
public class SomeOtherSite
{
public string Name { get; set; }
public string URL { get; set; }
}
And this is my code:
static void Main()
{
var handler = new HttpClientHandler();
handler.Credentials = new NetworkCredential("MyUsername", "MyPassword");
var client = new HttpClient(handler);
client.BaseAddress = new Uri("https://sitename.com:PortNumber/");
var formatters = new List<MediaTypeFormatter>()
{
new XmlMediaTypeFormatter(){ UseXmlSerializer = true }
};
var myApi = new MYAPI();
HttpResponseMessage response = client.GetAsync("/api/mysites").Result;
if (response.IsSuccessStatusCode)
{
myApi = response.Content.ReadAsAsync<MYAPI>(formatters).Result;
}
}
Now the myApi only has object for SomeOtherSite but the list for the MySite is empty.
Would someone please tell me how I should deserialize this response correctly?
Should I be creating custom media formatter? I have no idea of it by the way.
Also would you please tell me how to model that Resource attribute coming in the response?
And I can't change anything in the WebAPI server. I just need to consume the data from it and use it elsewhere.
Thank You so much!
I solved this after some really good direction from: https://stackoverflow.com/users/1124565/amura-cxg Much Thanks!
The solution was to annotate all the properties with XMLAttributes. And it correctly deserialized the response. And as for the Resource attribute, all I needed was [XmlAttribute(AttributeName="Resource")]
The rest of the source code works as is.
[XmlRoot(ElementName="MYAPI")]
public class MYAPI
{
[XmlElement(ElementName="MySite")]
public List<MySite> MySite { get; set; }
[XmlElement(ElementName="SomeOtherSite")]
public SomeOtherSite SomeOtherSite { get; set; }
}
public class MySite
{
[XmlElement(ElementName="Name")]
public string Name { get; set; }
[XmlElement(ElementName="URL")]
public string URL { get; set; }
[XmlElement(ElementName="SecondName")]
public string SecondName { get; set; }
[XmlAttribute(AttributeName="Resource")]
public string Resource { get; set; }
}
Plus, I didn't need any custom media formatter. And from one of the posts by https://stackoverflow.com/users/1855967/elisabeth , I learned that we should not touch the generated file from xsd.exe tool. So I explicitly set to use the XmlSerializer instead of the DataContractSerializer used by default:
var formatters = new List<MediaTypeFormatter>()
{
new XmlMediaTypeFormatter(){ UseXmlSerializer = true }
};
I have a wordpress.org locally hosted on my pc.
I've installed a wordpress plugin called json-api which let you retrieve posts from your wordpress site.
I'm running the following code:
var client = new RestClient(BlogArticlesUrl);
var request = new RestRequest();
request.Timeout = 5000;
request.RequestFormat = DataFormat.Json;
request.Method = Method.GET;
request.AddParameter("json", "get_tag_posts");
request.AddParameter("slug", "featured");
request.AddParameter("count", "3");
var articles = client.Execute<List<BlogArticleModel>>(request);
After executing the code, in the variable articles I have the following:
Inside the Content there are few keys but I would only like to convert 'posts' to a model in c#
How do I acheive that?
EDIT:
I have found a solution using newtonsoft for dot net
Newtonsoft.Json.JsonConvert.DeserializeObject<BlogArticleResponse>(articles.Content);
In RestSharp, the Content is what gets deserialized. So, the type you pass into the .Execute<T> method must be the same structure as the response.
In your case, it will look something like this:
public class BlogArticleResponse
{
public string status { get; set; }
public int count { get; set; }
public int pages { get; set; }
public BlogTag tag { get; set; }
...
}
public class BlogTag
{
public int id { get; set; }
public string slug { get; set; }
public string title { get; set; }
public string description { get; set; }
...
}
You can then execute the request like this:
var result = client.Execute<BlogArticleResponse>(request);
For more information, have a look at the documentation.
Couldn't find an answer from the other Json Serialization issue questions, so maybe someone can help me:
I'm getting a JSON object from a REST api and attempting to Deserialize it to an object. Below is the JSON Object I receive:
{"id":"6wVcZ9ZF67ECUQ8xuIjFT2",
"userId":"83ca0ab5-3b7c-48fe-8019-000320081b00",
"authorizations":["employee","API","trainer","queueAdmin","supervisor","workflowAdmin","realtimeManager","forecastAnalyst","qualityEvaluator","contactCenterManager","teamLead","personnelAdmin","telephonyAdmin","qualityAdmin","businessAdmin","businessUser","accountAdmin","dialerAdmin","contentManagementUser","contentManagementAdmin","admin","api","scriptDesigner","agent","user"],
"primaryAuthorization":"employee",
"thirdPartyOrgName":"in",
"username":"somebody",
"selfUri":"https://blahblahblah.com/api/v1/auth/sessions/6wVcZ9ZF67ECUQ8xuIjFT2"}
And my object I'm attempting to DeSerialize to:
[Serializable]
public class Session : BaseRequest, ISession
{
public Session(string url) : base(url)
{
}
#region Members
[JsonProperty(PropertyName = "userId")]
public string UserId { get; set; }
[JsonProperty(PropertyName = "authorizations")]
public object[] Authorizations { get; set; }
[JsonProperty(PropertyName = "primaryAuthorization")]
public string PrimaryAuthorization { get; set; }
[JsonProperty(PropertyName = "thirdPartyOrgName")]
public string ThirdPartyOrgName { get; set; }
[JsonProperty(PropertyName = "username")]
public string Username { get; set; }
[JsonProperty(PropertyName = "id")]
public string Id { get; set; }
[JsonProperty(PropertyName = "selfUri")]
public string SelfUri { get; set; }
#endregion
}
I simply make the web request and get the response stream using a stream reader and return the string. Pretty standard.
However, when I attempt to Deserialize into my Session object it always throws an error: Value Cannot be Null
var serializer = new JsonSerializer();
response = MakePostRequest(true);
var obj = serializer.Deserialize<Session>(new JsonTextReader(new StringReader(response)));
The response is the JSON string I get back from the web request and is exact to what I specified above.
I've done this before but normally I've been the one that designed the REST api. Not the case this time but I can't for the life of my figure out why this won't deserialize? I've specified the JSonProperty PropertyName to avoid issues with proper casing, is this not working right maybe? Any help is appreciated!
UDPATE
I think I found part of the problem. It is attempting to deserialize my base class which consists of :
public abstract class BaseRequest
{
protected BaseRequest(string apiUrl)
{
ApiUrl = apiUrl;
Request = (HttpWebRequest)WebRequest.Create(apiUrl);
}
public string ApiUrl { get; set; }
public string JsonPayload { get; set; }
public HttpWebRequest Request { get; private set; }
}
Is there any directive I can give to prevent it from doing so? Or will I need to refactor around this?
Below code works (using Json.Net):
var session = JsonConvert.DeserializeObject<Session>(json);
public class Session
{
public string Id { get; set; }
public string UserId { get; set; }
public List<string> Authorizations { get; set; }
public string PrimaryAuthorization { get; set; }
public string ThirdPartyOrgName { get; set; }
public string Username { get; set; }
public string SelfUri { get; set; }
}
EDIT
How should I tell it to ignore the base class?
var session = (Session)System.Runtime.Serialization.FormatterServices.GetSafeUninitializedObject(typeof(Session));
JsonConvert.PopulateObject(DATA, session);
But I don't think this is a nice way of doing it. Changing your design may be a better solution.
I've tested your code and it works fine, only change I made was removing the constructor, I take it that the serializer can't create an instance on the object for some reason, can you remove
public Session(string url) : base(url)
{
}
Your code works just fine for me but I haven't the BaseRequest source code so I made class with empty constructor.
IMO the exception is coming exactly from there. In the Session constructor the url parameter is null because your JSON object doesn't have url property. May be in the BaseRequest class you use this url param and you receive the Value Can't be Null error.
You can change just the name of parameter if this is the issue:
public Session(string selfUri ) : base(selfUri)
{
}
Check also if the 'response' variable is null. StringReader can throw this exception if you pass null to its constructor.
I am having trouble using the 'Paste XML as Classes' feature in VS2012 to properly deserialize XML results from a Rest call using Web API.
The XML response from the call looks like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<SCResponse>
<AccountId>86</AccountId>
<Administrator>false</Administrator>
<Email>6z#z.com</Email>
<FirstName>6z#z.com</FirstName>
<Label>false</Label>
<LastName>6z#z.com</LastName>
<link href="https://cnn.com" rel="news" title="News"/>
</SCResponse>
I copied this XML and used the handy new feature to paste this XML as classes:
namespace Models.account.response
{
[XmlRoot(ElementName = "SCResponse")] // I added this so I could name the object Account
[DataContract(Name = "SCResponse", Namespace = "")] // I added this as the namespace was causing me problems
public partial class Account
{
public byte AccountId { get; set; }
public bool Administrator { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public bool Label { get; set; }
public string LastName { get; set; }
[XmlElement("link")]
public SCResponseLink[] Link { get; set; }
}
[XmlType(AnonymousType = true)]
public partial class SCResponseLink
{
private string hrefField;
private string relField;
private string titleField;
[XmlAttribute)]
public string href { get; set; }
XmlAttribute]
public string rel { get; set; }
[XmlAttribute]
public string title { get; set; }
}
}
}
I call the REST endpoint like so:
string path = String.Format("account/{0}", id);
HttpResponseMessage response = client.GetAsync(path).Result; // Blocking call!
if (response.IsSuccessStatusCode)
{
// Parse the response body. Blocking!
account = response.Content.ReadAsAsync<Models.account.response.Account>().Result;
}
and examine the fields of the Account object -- all are null or defaulting to initialized values.
In my Global.asax.cs Application_Start method, I am registering the XML Serializer:
GlobalConfiguration.Configuration.Formatters.XmlFormatter.UseXmlSerializer = true;
A simpler way to handle this might be to use the RestSharp library, which will do all of the deserialization for you. This will simplify your calls, and you won't need the XML attributes on your model.
Take a look here for a good example of doing aync calls with RestSharp:
How should I implement ExecuteAsync with RestSharp on Windows Phone 7?
Hopefully this helps.