Im trying to deserialize an xml response from from Amazons simple db service.
The xml is like this.
<ListDomainsResponse xmlns="http://sdb.amazonaws.com/doc/2009-04-15/">
<ListDomainsResult>
<DomainName>Logging</DomainName>
<DomainName>teets</DomainName>
</ListDomainsResult>
<ResponseMetadata>
<RequestId>9d48122e-1ddf-8771-b771-b36599838ea0</RequestId>
<BoxUsage>0.0000071759</BoxUsage>
</ResponseMetadata>
</ListDomainsResponse>
And the object i'm trying to serialize into is this.
public class ListDomainsResponse : Response
{
public ListDomainsResult ListDomainsResult { get; set; }
}
public class ListDomainsResult
{
public List<String> DomainNames { get; set; }
public string NextToken { get; set; }
}
public class Response
{
public static string NameSpace = "http://sdb.amazonaws.com/doc/2009-04-15/";
public ResponseMetadata ResponseMetadata{ get; set; }
}
public class ResponseMetadata
{
public string RequestId { get ; set; }
public string BoxUsage { get; set; }
}
My problem is that the response metadata is getting deserialized correctly but the List of strings called DomainName is not being deserialized, it is empty each time.
Can someone see what i'm doing wrong.
Updated with more code
public async Task<Response> ListDomains (ListDomainsRequest request)
{
using (Client = new HttpClient ()) {
ListDomainsRequestMarshaller marshaller = new ListDomainsRequestMarshaller ();
marshaller.Configure (request);
HttpResponseMessage responseMessage = Client.GetAsync (marshaller.Marshal ()).Result;
ListDomainsResponseUnMarshaller unmarshaler = new ListDomainsResponseUnMarshaller (responseMessage);
return unmarshaler.Response;
}
}
public ListDomainsResponseUnMarshaller (HttpResponseMessage message)
{
XDocument doc = XDocument.Load (message.Content.ReadAsStreamAsync ().Result);
if (message.StatusCode.Equals (HttpStatusCode.OK)) {
Serializer = new XmlSerializer (typeof(ListDomainsResponse), Response.NameSpace);
Response = (ListDomainsResponse)Serializer.Deserialize (doc.CreateReader ());
Response.HttpStatusCode = message.StatusCode;
Response.ContentLength = (long)message.Content.Headers.ContentLength;
} else {
Serializer = new XmlSerializer (typeof(Response));
Response = (Response)Serializer.Deserialize (doc.CreateReader ());
Response.HttpStatusCode = message.StatusCode;
Response.ContentLength = (long)message.Content.Headers.ContentLength;
}
Serializer = null;
}
Adding Attributes helped
[System.Xml.Serialization.XmlTypeAttribute (Namespace = "http://sdb.amazonaws.com/doc/2009-04-15/")]
[System.Xml.Serialization.XmlRootAttribute ("ListDomainsResponse", Namespace = "http://sdb.amazonaws.com/doc/2009-04-15/")]
public class ListDomainsResponse : Response
{
public ListDomainsResult ListDomainsResult { get; set; }
}
[System.Xml.Serialization.XmlTypeAttribute (Namespace = "http://sdb.amazonaws.com/doc/2009-04-15/")]
[System.Xml.Serialization.XmlRootAttribute (Namespace = "http://sdb.amazonaws.com/doc/2009-04-15/")]
public class ListDomainsResult
{
[System.Xml.Serialization.XmlElementAttribute ("DomainName")]
public string[] DomainName { get; set; }
public string NextToken { get; set; }
}
Related
I am trying to call an API with HttpClient and deserialize the json to a response object. In this json, there is a list of trivia question objects. When I set the object to the deserialized object, the list stays empty.
I've checked if HttpClient works, it does, I also tried using JsonConvert.
These are the TriviaQuestion and Response classes:
public class TriviaQuestion
{
public string Category { get; set; }
public string Type { get; set; }
public string Difficulty { get; set; }
public string Question { get; set; }
public string CorrectAnswer { get; set; }
public List<string> IncorrectAnswers { get; set; }
public override string ToString()
{
return $"Question: {Question}";
}
}
public class Response
{
public int ResponseCode { get; set; }
public List<TriviaQuestion> Questions { get; set; }
public Response()
{
Questions = new List<TriviaQuestion>();
}
}
This is the code for deserializing
private static HttpClient client = new HttpClient();
private static string URL = "https://opentdb.com/api.php";
private static string urlParameters = "?amount=1";
static void Main()
{
RunAsync().GetAwaiter().GetResult();
}
static async Task RunAsync()
{
client.BaseAddress = new Uri(URL);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
Response response = new Response();
try
{
response = await GetResponseAsync(urlParameters);
ShowResponse(response);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Console.ReadLine();
}
static async Task<Response> GetResponseAsync(string path)
{
Response response = new Response();
//string responseString = "";
HttpResponseMessage httpResponse = await client.GetAsync(path);
if (httpResponse.IsSuccessStatusCode)
{
//responseString = httpResponse.Content.ReadAsStringAsync().Result;
response = await httpResponse.Content.ReadAsAsync<Response>();
}
//response = JsonConvert.DeserializeObject<Response>(responseString);
return response;
}
I expect to get a list of trivia question objects, but the list stays on count = 0. If I print out the jsonString I'm getting this is the result:
{
"response_code":0,
"results": [
{
"category":"Entertainment: Video Games",
"type":"multiple",
"difficulty":"medium",
"question":"In Need for Speed: Underground, what car does Eddie drive?",
"correct_answer":"Nissan Skyline GT-R (R34)",
"incorrect_answers": [
"Mazda RX-7 FD3S",
"Acura Integra Type R",
"Subaru Impreza 2.5 RS"
]
}]
}
Thanks for helping!
Your Response class is slightly wrong. It does not match the JSON you posted.
public List<TriviaQuestion> Questions { get; set; }
should be:
public List<TriviaQuestion> Results { get; set; }
Additionally, as your JSON has snake casing, to capture the response_code, correct_answer and incorrect_answers values you will need to either decorate your class properties with JsonProperty attributes i.e. [JsonProperty(PropertyName = "incorrect_answers")] or you can use a ContractResolver:
var contractResolver = new DefaultContractResolver
{
NamingStrategy = new SnakeCaseNamingStrategy()
};
var response = JsonConvert.DeserializeObject<Response>(json, new JsonSerializerSettings
{
ContractResolver = contractResolver
});
So your full classes would be:
public class TriviaQuestion
{
public string Category { get; set; }
public string Type { get; set; }
public string Difficulty { get; set; }
public string Question { get; set; }
// only need this if not using the ContractResolver
[JsonProperty(PropertyName = "correct_answer")]
public string CorrectAnswer { get; set; }
// only need this if not using the ContractResolver
[JsonProperty(PropertyName = "incorrect_answers")]
public List<string> IncorrectAnswers { get; set; }
}
public class Response
{
// only need this if not using the ContractResolver
[JsonProperty(PropertyName = "response_code")]
public int ResponseCode { get; set; }
public List<TriviaQuestion> Results { get; set; }
}
Then you will be able to deserialize:
var json = "{\r\n \"response_code\":0,\r\n \"results\": [\r\n { \r\n \"category\":\"Entertainment: Video Games\",\r\n \"type\":\"multiple\",\r\n \"difficulty\":\"medium\",\r\n \"question\":\"In Need for Speed: Underground, what car does Eddie drive?\",\r\n \"correct_answer\":\"Nissan Skyline GT-R (R34)\",\r\n \"incorrect_answers\": [\r\n \"Mazda RX-7 FD3S\",\r\n \"Acura Integra Type R\",\r\n \"Subaru Impreza 2.5 RS\"\r\n ]\r\n }]\r\n}";
// if using JsonProperty attributes
var response = JsonConvert.DeserializeObject<Response>(json);
// or
// if using ContractResolver
var contractResolver = new DefaultContractResolver
{
NamingStrategy = new SnakeCaseNamingStrategy()
};
var response = JsonConvert.DeserializeObject<Response>(json, new JsonSerializerSettings
{
ContractResolver = contractResolver
});
the returned JSON into a Response:
var json = await httpResponse.Content.ReadAsStringAsync();
response= JsonConvert.DeserializeObject<Response>(json);
I have faced this problem many times when I want to build a parent-child relationship class.
I have a base AuthenticateRequest class. In my case, I have 2 child requests but they have own logic to GetContent().
It doesn't not really fall into Composite Pattern nor Liskov Substitution as the base method is unique and called.
Which design pattern should I use?
public class AuthenticateRequest
{
public string Url { get; set; }
public string ContentType { get; set; }
public string Method { get; set; }
public virtual HttpContent GetContent()
{
return new StringContent("");
}
}
public class SoapAuthenticateRequest : AuthenticateRequest
{
public string SoapMethodName { get; set; }
public string SoapAction { get; set; }
public string KeyForUserNameParameter { get; set; }
public string ValueForUserNameParameter { get; set; }
public string KeyForPasswordParameter { get; set; }
public string ValueForPasswordParameter { get; set; }
public override HttpContent GetContent()
{
var methodName = this.SoapMethodName;
var keyUsername = this.KeyForUserNameParameter;
var keyPwd = this.KeyForPasswordParameter;
var valueUsername = this.ValueForUserNameParameter;
var valuePwd = this.ValueForPasswordParameter;
var soapAction = this.SoapAction ?? #"http://tempuri.org/";
var soap = $#"<soap:Envelope xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/""><soap:Body><{methodName} xmlns=""{soapAction}""><{keyUsername}>{valueUsername}</{keyUsername}><{keyPwd}>{valuePwd}</{keyPwd}></{methodName}></soap:Body></soap:Envelope>";
return new StringContent(soap, Encoding.UTF8, ContentTypes.XmlSoap);
}
}
public class JsonAuthenticateRequest : AuthenticateRequest
{
public string SoapMethodName { get; set; }
public string SoapAction { get; set; }
public Dictionary<string, string> ParameterKeyValues { get; set; }
public override HttpContent GetContent()
{
var json = JsonConvert.SerializeObject(ParameterKeyValues);
return new StringContent(json, Encoding.UTF8, ContentTypes.Json);
}
}
public async Task<AuthenticateResponse> Authenicate(AuthenticateRequest request)
{
var requestMsg = new HttpRequestMessage
{
RequestUri = new Uri(request.Url),
Method = new HttpMethod(request.Method.ToString()),
Content = request.GetContent(),
};
var responseMsg = await _httpClient.SendAsync(requestMsg).ConfigureAwait(false);
var responseContent = await responseMsg.Content.ReadAsStringAsync().ConfigureAwait(false);
return new AuthenticateResponse
{
Message = responseContent,
IsSuccess = Regex.Match(responseContent, (string)request.RegexForValidUser).Success
};
}
Did you see Factory pattern?
But for your problem, to require the class having his proper implementation. You can just simply use the abstract keyword like this
public abstract class AuthenticateRequest
{
public abstract HttpContent GetContent();
}
public class SoapAuthenticateRequest : AuthenticateRequest
{
public override HttpContent GetContent()
{
// your logic
return new StringContent(soap, Encoding.UTF8, ContentTypes.XmlSoap);
}
}
public class JsonAuthenticateRequest : AuthenticateRequest
{
public override HttpContent GetContent()
{
var json = JsonConvert.SerializeObject(ParameterKeyValues);
return new StringContent(json, Encoding.UTF8, ContentTypes.Json);
}
}
i have an application that has to deserialize an array of data wrapped in a "results" Root Object, using Netwonsoft.Json package from NuGet
The Json string is exactly this:
{"results":[{"Coin":"SBD","LP":0.000269,"PBV":-54.36,"MACD1M":true,"MACD30M":true,"MACD1H":true,"MACD1D":true},{"Coin":"XMR","LP":0.027135,"PBV":11.44,"MACD1M":true,"MACD30M":true,"MACD1H":true,"MACD1D":true}]}
This Json string is created from a Console App i made, i wanted it to look like this https://bittrex.com/Api/v2.0/pub/market/GetTicks?marketName=BTC-NEO&tickInterval=hour
My class looks like this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WindowsFormsApp2
{
public class Result
{
public string Coins { get; set; }
public decimal LastPrice { get; set; }
public decimal PercentBuyVolume { get; set; }
}
public class RootObject
{
public List<Result> results { get; set; }
}
}
In the Main form i have a function to download from a URL that Json (i have XAMPP running Apache) and deserialize it in an array. And it looks like this:
private void DownloadBittrexData()
{
int PanelID = 0;
var Coin = new List<string>();
var LastPrice = new List<decimal>();
var PercentBuyVolume = new List<decimal>();
var MACD1M = new List<bool>();
var MACD30M = new List<bool>();
var MACD1H = new List<bool>();
var MACD1D = new List<bool>();
var client = new WebClient();
var URL = client.DownloadString("http://localhost/test.json");
Console.WriteLine("Json String from URL: " + URL);
var dataDeserialized = JsonConvert.DeserializeObject<RootObject>(URL);
foreach (var data in dataDeserialized.results)
{
Coin.Add(data.Coins);
LastPrice.Add(data.LastPrice);
PercentBuyVolume.Add(data.PercentBuyVolume);
}
int sizeOfArrayClose = Coin.Count - 1;
for (int i = 0; i <= sizeOfArrayClose; i++)
{
Console.WriteLine("Coin: " + Coin[i]);
Console.WriteLine("Lastprice: " + LastPrice[i]);
Console.WriteLine("PBV: " + PercentBuyVolume[i]);
}
}
Newtonsoft.Json is of course declared at the beginning of the form together with System.Net
using System.Net;
using Newtonsoft.Json;
The output looks like this:
Json String from URL: {"results":[{"Coin":"SBD","LP":0.000269,"PBV":-54.36,"MACD1M":true,"MACD30M":true,"MACD1H":true,"MACD1D":true},{"Coin":"XMR","LP":0.027135,"PBV":11.44,"MACD1M":true,"MACD30M":true,"MACD1H":true,"MACD1D":true}]}
Coin:
Lastprice: 0
PBV: 0
Coin:
Lastprice: 0
PBV: 0
It's like it fails to deserialize it after downloading it.
What should i do? Thank you very much.
Your property names don't map to the field names in the JSON. You could rename your C# properties to match the JSON, but it would make for unreadable downstream code.
Instead, you should map your properties (with nice, readable names) to the names that appear in the JSON, using JsonPropertyAttribute:
public class Result
{
public string Coin { get; set; } //didn't bother here: changed property name to Coin
[JsonProperty("LP")]
public decimal LastPrice { get; set; }
[JsonProperty("PBV")]
public decimal PercentBuyVolume { get; set; }
}
your model should be like this for deserialize json
public class Result
{
public string Coin { get; set; }
public double LP { get; set; }
public double PBV { get; set; }
public bool MACD1M { get; set; }
public bool MACD30M { get; set; }
public bool MACD1H { get; set; }
public bool MACD1D { get; set; }
}
public class RootObject
{
public List<Result> results { get; set; }
}
LastPrice and PercentBuyVolume are not available in your model that's the reason it's getting an error.
I tried your exact code on my system and I was able to retrieve the result as expected. Hope this helps, It's easy to understand.
Here is the main class
static void Main(string[] args)
{
RootObject configfile = LoadJson();
foreach (var tResult in configfile.results)
{
Console.WriteLine("Coin: " + tResult.Coin);
Console.WriteLine("Lastprice: " + tResult.LP);
Console.WriteLine("PBV: " + tResult.PBV);
}
Console.ReadLine();
}
LoadJson Function would be
private static RootObject LoadJson()
{
string json = "{\"results\":[{\"Coin\":\"SBD\",\"LP\":0.000269,\"PBV\":-54.36,\"MACD1M\":true,\"MACD30M\":true,\"MACD1H\":true,\"MACD1D\":true},{\"Coin\":\"XMR\",\"LP\":0.027135,\"PBV\":11.44,\"MACD1M\":true,\"MACD30M\":true,\"MACD1H\":true,\"MACD1D\":true}]}";
RootObject configs = Deserialize<RootObject>(json);
return configs;
}
and Deserialize function would be
private static T Deserialize<T>(string json)
{
T unsecureResult;
string _DateTypeFormat = "yyyy-MM-dd HH:mm:ss";
DataContractJsonSerializerSettings serializerSettings = new DataContractJsonSerializerSettings();
DataContractJsonSerializer serializer;
MemoryStream ms;
unsecureResult = default(T);
serializerSettings.DateTimeFormat = new System.Runtime.Serialization.DateTimeFormat(_DateTypeFormat);
serializer = new DataContractJsonSerializer(typeof(T));
ms = new MemoryStream(Encoding.Unicode.GetBytes(json));
unsecureResult = (T)serializer.ReadObject(ms);
return unsecureResult;
}
and Now your Datamodel would be
public class Result
{
public string Coin { get; set; }
public double LP { get; set; }
public double PBV { get; set; }
public bool MACD1M { get; set; }
public bool MACD30M { get; set; }
public bool MACD1H { get; set; }
public bool MACD1D { get; set; }
}
public class RootObject
{
public List<Result> results { get; set; }
}
I am looking to deserialize data and place it into a generic class from a response from Azure.
<ServiceResources xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/windowsazure">
<ServiceResource>
<Name>Airport1</Name>
<Type>Microsoft.SqlAzure.FirewallRule</Type>
<State>Normal</State>
<SelfLink>https://management.core.windows.net:xxx/xxx/services/sqlservers/servers/xxx/firewallrules/Airport1</SelfLink>
<ParentLink>https://management.core.windows.net:xxxx/services/sqlservers/servers/xxx</ParentLink>
<StartIPAddress>000.000.000.000</StartIPAddress>
<EndIPAddress>2000.000.000.000</EndIPAddress>
</ServiceResource>
There are several objects I need to deserialze into my class.
[Serializable, XmlRoot(ElementName = "ServiceResource", Namespace = "http://schemas.microsoft.com/windowsazure/")]
public class ServiceResource
{
[XmlElement("Name")]
public string Name { get; set; }
[XmlElement("Type")]
public string Type { get; set; }
[XmlElement("State")]
public string State { get; set; }
[XmlElement("SelfLink")]
public string SelfLink { get; set; }
[XmlElement("ParentLink")]
public string ParentLink { get; set; }
[XmlElement("StartIPAddress")]
public string StartIPAddress { get; set; }
[XmlElement("EndIPAddress")]
public string EndIPAddress { get; set; }
}
I have tried several different ventures into this and can't nail it. I have used the xmlSerializer but hit blocks on that.
using (var responseStreamReader = new StreamReader(webResponse.GetResponseStream()))
{
XmlSerializer serializer = new XmlSerializer(typeof(ServiceResource));
ServiceResource deserialized = (ServiceResource)serializer.Deserialize(responseStreamReader);
}
Any help would be gratefully accepted.
Answer
The Azure REST Api is returning a list of ServiceResource in the XML. So you need to encapsulate that into a class. Here is an example.
[XmlRoot(
ElementName = "ServiceResources",
Namespace = "http://schemas.microsoft.com/windowsazure")]
public class ServiceResources
{
public ServiceResources()
{
Items = new List<ServiceResource>();
}
[XmlElement("ServiceResource")]
public List<ServiceResource> Items { get; set; }
}
public class ServiceResource
{
[XmlElement("Name")]
public string Name { get; set; }
[XmlElement("Type")]
public string Type { get; set; }
[XmlElement("State")]
public string State { get; set; }
[XmlElement("SelfLink")]
public string SelfLink { get; set; }
[XmlElement("ParentLink")]
public string ParentLink { get; set; }
[XmlElement("StartIPAddress")]
public string StartIPAddress { get; set; }
[XmlElement("EndIPAddress")]
public string EndIPAddress { get; set; }
}
With those two classes, you can now do the following.
var response = request.GetResponse();
var message = string.Empty;
using (var responseStreamReader = new StreamReader(response.GetResponseStream()))
{
message = responseStreamReader.ReadToEnd();
}
var textReader = new StringReader(message);
var serializer = new XmlSerializer(typeof(ServiceResources));
var serviceResources =
serializer.Deserialize(textReader) as ServiceResources;
Demo Console App
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace DeserializeAzureXmlResponse
{
class Program
{
private static string certificateThumbprint = "19DAED4D4ABBE0D400DC33A6D99D00D7BBB24472";
private static string subscriptionId = "14929cfc-3501-48cf-a5c9-b24a7daaf694";
static string sqlServerName = "mvp2015";
static string managementUri = "https://management.core.windows.net";
static string sqlServerApi = "services/sqlservers/servers";
static string firewallRules = "firewallrules";
static void Main(string[] args)
{
var restUri = CreateRestUri();
var clientCert = GetX509FromPersonalStore();
var request = (HttpWebRequest)HttpWebRequest.Create(restUri);
request.Headers.Add("x-ms-version", "2012-03-01");
request.ClientCertificates.Add(clientCert);
var response = request.GetResponse();
var message = string.Empty;
using (var responseStreamReader = new StreamReader(response.GetResponseStream()))
{
message = responseStreamReader.ReadToEnd();
}
var textReader = new StringReader(message);
var serializer = new XmlSerializer(typeof(ServiceResources));
var serviceResources = serializer.Deserialize(textReader) as ServiceResources;
foreach (var sr in serviceResources.Items)
{
Console.WriteLine("Name".PadRight(20) + sr.Name);
Console.WriteLine("Type".PadRight(20) + sr.Type);
Console.WriteLine("State".PadRight(20) + sr.State);
Console.WriteLine("SelfLink".PadRight(20) + sr.SelfLink);
Console.WriteLine("ParentLink".PadRight(20) + sr.ParentLink);
Console.WriteLine("StartIP".PadRight(20) + sr.StartIPAddress);
Console.WriteLine("EndIP".PadRight(20) + sr.EndIPAddress);
Console.WriteLine("+++++++++++");
}
Console.ReadLine();
}
static Uri CreateRestUri()
{
// https://management.core.windows.net/{subscriptionID}/services/sqlservers/servers/{server}/firewallrules/
var builder = new StringBuilder();
builder.Append(managementUri + "/");
builder.Append(subscriptionId + "/");
builder.Append(sqlServerApi + "/");
builder.Append(sqlServerName + "/");
builder.Append(firewallRules + "/");
var uri = new Uri(builder.ToString());
return uri;
}
static X509Certificate GetX509FromPersonalStore()
{
// To view the personal store, press `Win + R` and then type `certmgr.msc`
var store = new X509Store(StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
var certificates = store.Certificates.Find(X509FindType.FindByThumbprint, certificateThumbprint, true);
var certificate = certificates[0];
store.Close();
return certificate;
}
}
[XmlRoot(
ElementName = "ServiceResources",
Namespace = "http://schemas.microsoft.com/windowsazure")]
public class ServiceResources
{
public ServiceResources()
{
Items = new List<ServiceResource>();
}
[XmlElement("ServiceResource")]
public List<ServiceResource> Items { get; set; }
}
public class ServiceResource
{
[XmlElement("Name")]
public string Name { get; set; }
[XmlElement("Type")]
public string Type { get; set; }
[XmlElement("State")]
public string State { get; set; }
[XmlElement("SelfLink")]
public string SelfLink { get; set; }
[XmlElement("ParentLink")]
public string ParentLink { get; set; }
[XmlElement("StartIPAddress")]
public string StartIPAddress { get; set; }
[XmlElement("EndIPAddress")]
public string EndIPAddress { get; set; }
}
}
Output
Name My-House
Type Microsoft.SqlAzure.FirewallRule
State Normal
SelfLink https://management.core.windows.net/14929cfc-35
ParentLink https://management.core.windows.net/14929cfc-35
StartIP 123.435.234.643
EndIP 123.435.234.643
+++++++++++
Name AllowAllWindowsAzureIps
Type Microsoft.SqlAzure.FirewallRule
State Normal
SelfLink https://management.core.windows.net/14929cfc-35
ParentLink https://management.core.windows.net/14929cfc-35
StartIP 0.0.0.0
EndIP 0.0.0.0
+++++++++++
See Also
Is it possible to deserialize XML into List<T>?
List Firewall Rules
I am assuming you are trying to deserialize the whole object graph. Given xml has root node ServiceResources which contains ServiceResource. You have two options, you can mimic the whole xml into classes and desrialize; or just get the inner node of ServiceResource and deserialize that part.
If you use first option, then you would need to store ServiceResource inside another class which has mapped collections property with XmlElement name set to "ServiceResource", e.g.:
[XmlType(Namespace="http://schemas.microsoft.com/windowsazure")]
[XmlRoot(Namespace="http://schemas.microsoft.com/windowsazure")]
public class ServiceResource
{
public string Name { get; set; }
public string Type { get; set; }
public string State { get; set; }
public string SelfLink { get; set; }
public string ParentLink { get; set; }
public string StartIPAddress { get; set; }
public string EndIPAddress { get; set; }
}
[XmlType(Namespace="http://schemas.microsoft.com/windowsazure")]
[XmlRoot(Namespace="http://schemas.microsoft.com/windowsazure")]
public class ServiceResources
{
[XmlElement("ServiceResource")]
public List<ServiceResource> ServiceResource { get; set; }
}
With that you should be able to deserialize directly. Container class has the collections mapped to the ServiceResource element, which will load all of the nodes for service resource. Note: deserialization target type is now "ServiceResources" not the inner type "ServiceResource"
using (var responseStreamReader = new StreamReader(webResponse.GetResponseStream()))
{
XmlSerializer serializer = new XmlSerializer(typeof(ServiceResources));
ServiceResources deserialized = (ServiceResources)serializer.Deserialize(responseStreamReader);
//you can access each item in loop
foreach(var res in deserialized.ServiceResource)
{
//access items e.g.
var name = res.Name;
}
}
I have a Api link that returns this Json structure. In the code I request this Api link and then deserialize it into a list. This is done without problems. But if the Api returns more than 50 "counts" it creates another page. How do i get around to loop through all pages and add everything to the existing list?
In the case i linked there will be 38 pages. All need to be added to the list.
Call
// spidyApiUrl = http://www.gw2spidy.com/api/v0.9/json/item-search/iron/1
var spidyApi_idByName_result = api_Handler.objFromApi_idToName(spidyApiUrl);
Function with the return
public RootObject objFromApi_idToName(string url)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
WebResponse response = request.GetResponse();
using (Stream responseStream = response.GetResponseStream())
{
StreamReader reader = new StreamReader(responseStream, Encoding.UTF8);
var jsonReader = new JsonTextReader(reader);
var serializer = new JsonSerializer();
//return serializer.Deserialize<RootObject>(jsonReader);
RootObject rootObject = serializer.Deserialize<RootObject>(jsonReader);
if (rootObject.count > 0) { return rootObject; }
else { return null; }
}
}
And ofcourse i also have the get; set; classes.
How do I loop through all pages (if mutliple pages exist, which doesnt have to) and add these to the same object list.
You need continue downloading the data until page == last_page
As you get each page of data you then add the new set of results to the original rootObject's results property with AddRange
I also changed the url that gets passed into the function from
http://www.gw2spidy.com/api/v0.9/json/item-search/iron/1
to
http://www.gw2spidy.com/api/v0.9/json/item-search/iron
This allows me to add the page numbers to the url to get all the pages
http://www.gw2spidy.com/api/v0.9/json/item-search/iron/1
http://www.gw2spidy.com/api/v0.9/json/item-search/iron/2
.....
http://www.gw2spidy.com/api/v0.9/json/item-search/iron/38
Code:
public class Result
{
public int data_id { get; set; }
public string name { get; set; }
public int rarity { get; set; }
public int restriction_level { get; set; }
public string img { get; set; }
public int type_id { get; set; }
public int sub_type_id { get; set; }
public string price_last_changed { get; set; }
public int max_offer_unit_price { get; set; }
public int min_sale_unit_price { get; set; }
public int offer_availability { get; set; }
public int sale_availability { get; set; }
public int sale_price_change_last_hour { get; set; }
public int offer_price_change_last_hour { get; set; }
}
public class RootObject
{
public int count { get; set; }
public int page { get; set; }
public int last_page { get; set; }
public int total { get; set; }
public List<Result> results { get; set; }
}
class Program
{
static void Main(string[] args)
{
objFromApi_idToName("http://www.gw2spidy.com/api/v0.9/json/item-search/iron");
}
public static RootObject objFromApi_idToName(string url)
{
RootObject rootObject = null;
RootObject tempRootObject = null;
int page = 1;
do
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url + "/" + page);
WebResponse response = request.GetResponse();
using (Stream responseStream = response.GetResponseStream())
{
StreamReader reader = new StreamReader(responseStream, Encoding.UTF8);
var jsonReader = new JsonTextReader(reader);
var serializer = new JsonSerializer();
//return serializer.Deserialize<RootObject>(jsonReader);
tempRootObject = serializer.Deserialize<RootObject>(jsonReader);
if (rootObject == null)
{
rootObject = tempRootObject;
}
else
{
rootObject.results.AddRange(tempRootObject.results);
rootObject.count += tempRootObject.count;
}
}
page++;
} while (tempRootObject != null && tempRootObject.last_page != tempRootObject.page);
return rootObject;
}
}
Are you using Web API? If so, could you try something like this?
public RootObject objFromApi_idToName(string url)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("<your uri here>");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync("<uri extention here>");
if (response.IsSuccessStatusCode)
{
string jsonStr = await response.Content.ReadAsStringAsync();
var deserializedResponse = JsonConvert.DeserializeObject<List<your model class here>>(jsonStr);
return deserializedResponse;
}
}