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...
}
Related
Im new to System.Net with C#
I want a way to get info from this website api: https://fn-api.glitch.me/api/aes
from its json to a C# string
I have this so far
I don't know how to get each item and where to put the url (im really new to this).
I want the url in a string:
public class Data
{
public string build { get; set; }
public string netCL { get; set; }
public string manifestID { get; set; }
public string aes { get; set; }
}
public class RootObject
{
public Data data { get; set; }
}
Okay, this is how you get about it. I am showing you an example using HttpClient to first read the content from the API and then de-serialize it using Newtonsoft package.
HttpClient class:
public class HttpClientFactory
{
private string webServiceUrl = "https://fn-api.glitch.me/";
public HttpClient CreateClient()
{
var client = new HttpClient();
SetupClientDefaults(client);
return client;
}
protected virtual void SetupClientDefaults(HttpClient client)
{
//This is global for all REST web service calls
client.Timeout = TimeSpan.FromSeconds(60);
client.BaseAddress = new Uri(webServiceUrl);
}
}
Your Model class:
public class Data
{
public string build { get; set; }
public string netCL { get; set; }
public string manifestID { get; set; }
public string aes { get; set; }
}
public class RootObject
{
public Data data { get; set; }
}
Now, you can call this class and create an instance of the HttpClient like this:
public RootObject InvokeAPI()
{
RootObject apiresponse = new RootObject();
string result = string.Empty;
HttpClientFactory clientFactory = new HttpClientFactory();
var client = clientFactory.CreateClient();
HttpResponseMessage response = client.GetAsync("api/aes").Result;
if (response.IsSuccessStatusCode)
{
result = response.Content.ReadAsStringAsync().Result;
apiresponse = JsonConvert.DeserializeObject<RootObject>(result);
}
return apiresponse;
}
Hope this helps you out.
EDIT:
As per your code, you need to call the API on your Button click:
private void metroButton2_Click_1(object sender, EventArgs e)
{
//You need to invoke the API method !!!!
var apiresponse=InvokeAPI();
metroTextBox1.Text = apiresponse.data.aes;
}
Be sure to put try-catch blocks on your code for error handling.
I'd recommend using a 3rd party library like RestSharp. It'll give you a client that's easy to work with and does the converting into objects automatically.
Alternatively you could use the WebClient and download the JSON. Using something like Json.NET allows you to deserialize the JSON into an object.
Easiest way to read from a URL into a string in .NET
I use JSON.Net.
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.
I've been struggling for quite a while, but now I managed to successfully pull JSON data from a web API.
My code so far (only a test snippet thus far):
var url = "http://www.trola.si/bavarski";
string text;
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
request.Method = WebRequestMethods.Http.Get;
request.Accept = "application/json";
var json = (HttpWebResponse)request.GetResponse();
using (var sr = new StreamReader(json.GetResponseStream()))
{
text = sr.ReadToEnd();
}
As far as pulling data goes, this is ok, right?
Well here's where it gets a bit confusing. There are a lot resources online and all of them differ quite a bit. Do I need to create a class that will hold the data and { get; set; } too?
Would RESTsharp or Json.NET make my job easier? Any suggestions are appreciated.
You do not need any third party JSON libs.
Get the data to a string. You have already done this.
Create your data classes. I like igrali's idea of using Visual Studio to do it. But if the data is simple just write the class yourself:
[DataContract]
public class PersonInfo
{
[DataMember]
public string FirstName { get; set; }
[DataMember]
public string LastName { get; set; }
}
Deserialize from the string to the classes:
I like to use this generic helper:
public static T Deserialize<T>(string json)
{
using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(json)))
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
T obj = (T)serializer.ReadObject(stream);
return obj;
}
}
And then call it like this:
PersonInfo info = (PersonInfo)JsonHelper.Deserialize<PersonInfo>(s);
First of all, you will want to create classes that represent the JSON model you received. There's more than one way to do it - you can use json2csharp or even better, the Visual Studio feature called Paste JSON As Classes (find it in: Edit -> Paste Special -> Paste JSON As Classes).
Once you have the classes, you can use Json.NET to help you with the JSON response. You probably want to deserialize the string (JSON) you received to C# objects. To do it, you can just call the JsonConvert.DeserializeObject method.
var myObject = JsonConvert.DeserializeObject<MyClass>(json);
where MyClass is any kind of type you are deserializing to.
There's a WebApi client that will take care of all of the serialization for you.
http://www.asp.net/web-api/overview/advanced/calling-a-web-api-from-a-net-client
Here's a sample:
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:9000/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// New code:
HttpResponseMessage response = await client.GetAsync("api/products/1");
if (response.IsSuccessStatusCode)
{
Product product = await response.Content.ReadAsAsync<Product>();
Console.WriteLine("{0}\t${1}\t{2}", product.Name, product.Price, product.Category);
}
}
Json.net helps a lot with this. You can deserialize to anonymous types or POCO objects. I hope below solution helps you get started.
async Task Main()
{
using (var client = new HttpClient())
{
using (var request = new HttpRequestMessage())
{
request.RequestUri = new Uri("http://www.trola.si/bavarski");
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
request.Method = HttpMethod.Get;
var result = await client.SendAsync(request);
string jsonStr = await result.Content.ReadAsStringAsync();
Result obj = JsonConvert.DeserializeObject<Result>(jsonStr);
obj.Dump();
}
}
}
// Define other methods and classes here
public class Result
{
[JsonProperty(PropertyName = "stations")]
public Station[] Stations { get; set;}
}
public class Station
{
[JsonProperty(PropertyName = "number")]
public string Number { get; set; }
[JsonProperty(PropertyName = "name")]
public string Name { get; set; }
[JsonProperty(PropertyName = "buses")]
public Bus[] Buses { get; set; }
}
public class Bus
{
[JsonProperty(PropertyName = "direction")]
public string Direction { get; set; }
[JsonProperty(PropertyName = "number")]
public string Number { get; set; }
[JsonProperty(PropertyName = "arrivals")]
public int[] Arrivals { get; set; }
}
I have read one answer on atlassian https://answers.atlassian.com/questions/79902/using-httpclient-c-to-create-a-jira-issue-via-rest-generates-bad-request-response where one user created a JIRA issue by the following code. I adapted it but get an error by using a self-build class issue with ObjectContent
Http.HttpContent content = new Http.ObjectContent<Issue>(data, jsonFormatter);
The compiler wont accept it. Does anybody know why?
public string CreateJiraIssue()
{
string data= #"{ ""fields"": {
""project"":
{
""key"": ""HELP""
},
""summary"": ""Test Ticket"",
""description"": ""Creating of an issue using project keys and issue type names using the REST API"",
""issuetype"": {
""name"": ""Ticket""
},
""assignee"": { ""name"": ""user"" }
}
}";
string postUrl = "https://xxx.jira.com/rest/api/2/";
System.Net.Http.HttpClient client = new System.Net.Http.HttpClient();
client.BaseAddress = new System.Uri(postUrl);
byte[] cred = UTF8Encoding.UTF8.GetBytes("username:password");
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(cred));
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
System.Net.Http.Formatting.MediaTypeFormatter jsonFormatter = new System.Net.Http.Formatting.JsonMediaTypeFormatter();
System.Net.Http.HttpContent content = new System.Net.Http.ObjectContent<Issue>(data, jsonFormatter);
System.Net.Http.HttpResponseMessage response = client.PostAsync("issue", content).Result;
if (response.IsSuccessStatusCode)
{
string result = response.Content.ReadAsStringAsync().Result;
return result;
}
else
{
return response.StatusCode.ToString();
}
And using
namespace IOnotification_System
{
public class Issue
{
public Fields fields { get; set; }
public Issue()
{
fields = new Fields();
}
}
public class Fields
{
public Project project { get; set; }
public string summary { get; set; }
public string description { get; set; }
public Assignee assignee { get; set; }
public IssueType issuetype { get; set; }
public Fields()
{
project = new Project();
issuetype = new IssueType();
}
}
public class Project
{
public string key { get; set; }
}
public class IssueType
{
public string name { get; set; }
}
public class Assignee
{
public string name { get; set; }
}
}
EDIT
The message clearly says that System.Net.Http.ObjectContent() expects an Issue object for its first parameter. I expect there is another message right after that saying that there is no conversion possible from a string to an Issue.
You are passing a string to a method that expects an Issue object. The formatter is used to convert an Issue object to a Json string.
You already have the string, so there is no point in trying to convert it. You only need the formatter if you have an Issue instance which you want to convert to a Json string. You can use the StringContent class and use its Headers property to add any headers not already set on the client, eg:
var content=new StringContent(data);
Original
What is the error message and what kind of project are you using? The System.Net.Http.Formatting namespace is part of ASP.NET Web API. Are you building an ASP.NET application, a console application, something else?
Unless you ARE building an ASP.NET site this code won't work. If your only issue is how to parse Json requests, just use another Json deserialization class. Json.NET is a very popular choice.
In any case there is no reason to use a Json class to convert a string to an HttpContent object containing that exact same string. You can use the StringContent class and use its Headers property to add any headers not already set on the client.
The following does the magic:
var content = new StringContent(data, Encoding.UTF8, "application/json");
I'm using RestRequest to make a POST to web service. Response is in JSON format, but I get it in response.Content as ASCII, and Data is null. code is:
var request = new RestRequest(api, Method.POST);
request.RequestFormat = DataFormat.Json;
request.AddObject(data);
RestClient client = new RestClient("http://IP:PORT/proto");
client.ExecuteAsync<jLoginResponse>(request, (response) =>
{
var resource = response.Data;
});
and here response.Data is empty, and Content is
{"uid":"1234"}
jLoginResponse is declared as
[DataContract]
public class jLoginResponse
{
public string uid { get; set; }
}
but it's not getting deserialized automatically as it should.
The class should have the members marked with DataMember, like this:
[DataContract]
public class jLoginResponse
{
[DataMember]
public string uid { get; set; }
}