How to deserialize dictionary to an object in Xamarin Forms - c#

When I request to server, I receive a response as a dictionary:
{
data=({
gender=female;
id=3490074761922520002;
name=joy;
picture={
data={
url="";
};
};
},
{
gender=female;
id=8242568878166175182;
name="name";
picture={
data={
url="";
};
};
})
paging={
next="";
}
summary={
"total_count"=66;
};
}
How can I deserialize this response dictionary to an object?

[UPDATE ANSWER]
After 2 days searching for answer, I finally found the way to solve this issue.
Here is my code:
var err = new NSError();
var theJSONData = NSJsonSerialization.Serialize(response.Data, NSJsonWritingOptions.PrettyPrinted, out err);
var theJSONText = new NSString(theJSONData, NSStringEncoding.UTF8);
Console.WriteLine(theJSONText);
response.data as NSDictionary -> JSON string

Other way to desserealize is:
public async Task<List<Produto>> GetProdutosAsync()
{
try
{
string url = "http://www.something.com/api/produtos/";
var response = await client.GetStringAsync(url);
var produtos = JsonConvert.DeserializeObject<List<Produto>>(response);
return produtos;
}
catch (Exception ex)
{
throw ex;
}
}
Change produto for your type.

Related

C# REST Request Method falling out of try block into catch block when correcting typo

I've created this C# method to perform a JSON request getting the place_id from a text search. As far as I can tell the request itself works since I get a nullreferenceexception when trying to access non existent values where the message variable is set for example(Initially wrote "candidats" instead of "candidates"). However after correcting the typo it seems to only return from the catch block and I can't figure out why, what am I doing wrong here?
public async Task<string> GetPlaceAsync(string cityname, string countrycode)
{
string thereturn = "";
var client = new HttpClient();
var uri = new Uri("https://maps.googleapis.com/maps/api/place/findplacefromtext/json?input="+ cityname + "," + countrycode + "&inputtype=textquery&key="+apikey);
try
{
var response = await client.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadAsStringAsync();
var jsonData = (JObject)JsonConvert.DeserializeObject(result);
var status = jsonData["status"].Value<string>();
if (status != "ZERO_RESULTS")
{
var message = jsonData["candidates"]["place_id"].Value<string>();
System.Diagnostics.Debug.WriteLine(message);
return message;
}
else
thereturn = status;
}
else thereturn = "fail";
}
catch { thereturn = "Connection-error"; }
return thereturn;
}
edit: Since i sort of figured out what the problem is using David's answer of printing out the exception, we can move on to the next one which is not finding the place_id, here's the JSON data:
{
"candidates": [
{
"place_id": "ChIJFWGA6nAUz0URs78HFrUdA7c"
}
],
"status": "OK"
}

How to store only " in C# string

I want to get JSON from string and I need to extract " instead of \" in my code.
Here is my code that I want to use it in:
internal static string ReturnRedditJsonPage(string subredditname)
{
return
$"https://reddit.com/r/{subredditname}.json";
}
internal static Reddit ParseReddit(string subredditname)
{
WebResponse response = HttpWebRequest.CreateHttp(ReturnRedditJsonPage(subredditname)).GetResponse();
string responseContent = new StreamReader(response.GetResponseStream()).ReadToEnd().Replace("\\",#"\").Replace("\"",((char)0x0022).ToString()).Trim();
return JsonConvert.DeserializeObject<Reddit>(responseContent);
}
internal static Uri[] GetMemesLinks(string subredditname)
{
Reddit jsonData = ParseReddit(subredditname);
List<Uri> result = new List<Uri>();
foreach(Child child in jsonData.Data.Children)
{
result.Add(child.Data.Url);
}
return result.ToArray();
}
It gives me back JSON that I cannot parse because of \" in string instead of ". How can I fix it?
You can use JSON.NET coupled with a little LINQ magic to extract all URI(s) out of the sub-reddit API.
Here's a demo, tweak to your requirements:
internal static string ReturnRedditJsonURI(string SubRedditName)
{
return $"https://reddit.com/r/{SubRedditName}.json";
}
// Does a HTTP GET request to the external Reddit API to get contents and de-serialize it
internal static async Task<JObject> ParseReddit(string SubRedditName)
{
string exampleURI = ReturnRedditJsonURI(SubRedditName);
JObject response = new JObject();
using (HttpClient client = new HttpClient())
{
// Make the HTTP request now
HttpResponseMessage msg = await client.GetAsync(exampleURI);
// If HTTP 200 then go ahead and de-serialize
if (msg.IsSuccessStatusCode)
{
string responseBody = await msg.Content.ReadAsStringAsync();
response = JsonConvert.DeserializeObject<JObject>(responseBody);
}
}
return response;
}
// Driver method to extract the URI(s) out of the reddit response
internal static async Task<List<Uri>> GetRedditURI(string SubRedditName)
{
string subRedditName = "Metallica";
JObject redditData = await ParseReddit(SubRedditName);
List<Uri> redditURIList = new List<Uri>();
try
{
// TODO: instead of JObject use concrete POCO, but for now this seems to be it.
redditURIList = redditData["data"]?["children"]?
.Select(x => x["data"])
.SelectMany(x => x)
.Cast<JProperty>()
.Where(x => x.Name == "url")
.Select(x => x.Value.ToString())
.Select(x => new Uri(x, UriKind.Absolute)).ToList() ?? new List<Uri>();
return redditURIList;
}
catch (Exception ex)
{
return redditURIList;
}
}

Flurl PostUrlEncoded does GET instead of POST

I must be missing something very obvious, but I can't tell what. I have a DoLoginAsync like so:
private async Task DoLoginAsync(bool force = false)
{
try
{
if (client.Cookies.ContainsKey("user_credentials") && !force)
{
return;
}
var html = client.Request("login").GetStringAsync().Result;
var doc = new HtmlDocument();
doc.LoadHtml(html);
var csrf_token = doc.DocumentNode.SelectNodes("//meta[#name='csrf-token']").First().GetAttributeValue("content", string.Empty);
var values = new Dictionary<string, string>
{
{ "user_session[email]", user },
{ "user_session[password]", password },
{ "authenticity_token", csrf_token }
};
var result = await client.Request("user_session").PostUrlEncodedAsync(values);
}
catch (Exception e)
{
}
When I run this code in a test with a breakpoint in the catch clause I get an exception
Call failed with status code 404 (Not Found): GET http://www.whatever.com/user_session
WTF? I'm expecting PostUrlEncodedAsync to do a POST, not a GET. Anybody have an idea why this can happen?
The Flurl client is instantiated as client = new FlurlClient(BASE_URL).EnableCookies();
UPDATE
Tried the following test which fails with the same exception
[TestMethod]
public async Task TheTest()
{
var message = "";
try
{
var client = new FlurlClient("http://www.slimmemeterportal.nl/").EnableCookies();
var html = await client.Request("login").GetStringAsync();
var doc = new HtmlDocument();
doc.LoadHtml(html);
var csrf_token = doc.DocumentNode.SelectNodes("//meta[#name='csrf-token']").First().GetAttributeValue("content", string.Empty);
var values = new Dictionary<string, string>
{
{ "user_session[email]", "******" },
{ "user_session[password]", "******" },
{ "commit", "inloggen" }, // Not sure if this is actually needed, but it is in the website's request parameters.
{ "authenticity_token", csrf_token }
};
var result = await client.Request("user_session").PostUrlEncodedAsync(values);
}
catch (FlurlHttpException ex)
{
message = ex.Message;
}
Assert.AreEqual("Call failed with status code 404 (Not Found): POST http://www.slimmemeterportal.nl/user_session", message);
}
Mystery solved: As it turns out after some debugging with Wireshark, the website was returning HTTP status code 301. As explained here the default action is to follow the URI in the response's location header using a GET even if the original request was a POST.

Elasticsearch NEST update by script

I'm trying to update some fields by script. In Postman I'm sending the following request:
https://search.test.com/items_example/item/en_01_2/_update
the request's body:
{
"script": {
"inline": "ctx._source.Title = params.Title; ctx._source.Desc = params.Desc",
"params": {
"Title": "New Title",
"Desc": "New Desc"
}
}
}
But I have no idea how I can send this request using NEST. Can someone please help me?
Elasticsearch 5.4.1 version, NEST 5.6.1
Update it with your index settings & query
var elasticClient = new ElasticClient(settings);
var scriptParams = new Dictionary<string, object>
{
{"Title", "New Title"},
{"Desc", "New Desc"}
};
var response = elasticClient
.UpdateByQuery<dynamic>(q => q.Query(rq => rq.Term(....))
.Script(script =>
script.Inline(
$"ctx._source.Title = params.Title;" +
$"ctx._source.Desc = params.Desc ;"
)
.Params(scriptParams));
Edit: If you're looking for just Update, just change the syntax to
var response = elasticClient.Update<dynamic>(
"items_example/item/en_01_2" //This is your document path
, request => request.Script(
script =>
script.Inline(
$"ctx._source.Title = params.Title;" +
$"ctx._source.Desc = params.Desc ;"
)
.Params(scriptParams)));
If someone is here looking for the solution with NEST 6x version, please see below
public async Task UpdateId(MetaData request)
{
try
{
var scriptParams = new Dictionary<string, object>
{
{ "Id",request.Id}
};
var script = $"ctx._source.Id= params.Id;";
var indexResponse =
await EsClient.UpdateByQueryAsync<Doc>(
qd => qd.Index(request.IndexName)
.Conflicts(Conflicts.Proceed)
.Query(
rq => rq.Term("_id", request._id))
.Script(
s => s.Source(script)
.Params(scriptParams)
)
)
);
if (!indexResponse.IsValid)
{
}
}
catch (Exception ex)
{
}
}
[ElasticsearchType(Name = "_doc")]
public class Doc
{
}

C# Web API method returns 403 Forbidden

Solved!!! - See last edit.
In my MVC app I make calls out to a Web API service with HMAC Authentication Filterign. My Get (GetMultipleItemsRequest) works, but my Post does not. If I turn off HMAC authentication filtering all of them work. I'm not sure why the POSTS do not work, but the GETs do.
I make the GET call from my code like this (this one works):
var productsClient = new RestClient<Role>(System.Configuration.ConfigurationManager.AppSettings["WebApiUrl"],
"xxxxxxxxxxxxxxx", true);
var getManyResult = productsClient.GetMultipleItemsRequest("api/Role").Result;
I make the POST call from my code like this (this one only works when I turn off HMAC):
private RestClient<Profile> profileClient = new RestClient<Profile>(System.Configuration.ConfigurationManager.AppSettings["WebApiUrl"],
"xxxxxxxxxxxxxxx", true);
[HttpPost]
public ActionResult ProfileImport(IEnumerable<HttpPostedFileBase> files)
{
//...
var postResult = profileClient.PostRequest("api/Profile", newProfile).Result;
}
My RestClient builds like this:
public class RestClient<T> where T : class
{
//...
private void SetupClient(HttpClient client, string methodName, string apiUrl, T content = null)
{
const string secretTokenName = "SecretToken";
client.BaseAddress = new Uri(_baseAddress);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
if (_hmacSecret)
{
client.DefaultRequestHeaders.Date = DateTime.UtcNow;
var datePart = client.DefaultRequestHeaders.Date.Value.UtcDateTime.ToString(CultureInfo.InvariantCulture);
var fullUri = _baseAddress + apiUrl;
var contentMD5 = "";
if (content != null)
{
var json = new JavaScriptSerializer().Serialize(content);
contentMD5 = Hashing.GetHashMD5OfString(json); // <--- Javascript serialized version is hashed
}
var messageRepresentation =
methodName + "\n" +
contentMD5 + "\n" +
datePart + "\n" +
fullUri;
var sharedSecretValue = ConfigurationManager.AppSettings[_sharedSecretName];
var hmac = Hashing.GetHashHMACSHA256OfString(messageRepresentation, sharedSecretValue);
client.DefaultRequestHeaders.Add(secretTokenName, hmac);
}
else if (!string.IsNullOrWhiteSpace(_sharedSecretName))
{
var sharedSecretValue = ConfigurationManager.AppSettings[_sharedSecretName];
client.DefaultRequestHeaders.Add(secretTokenName, sharedSecretValue);
}
}
public async Task<T[]> GetMultipleItemsRequest(string apiUrl)
{
T[] result = null;
try
{
using (var client = new HttpClient())
{
SetupClient(client, "GET", apiUrl);
var response = await client.GetAsync(apiUrl).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
await response.Content.ReadAsStringAsync().ContinueWith((Task<string> x) =>
{
if (x.IsFaulted)
throw x.Exception;
result = JsonConvert.DeserializeObject<T[]>(x.Result);
});
}
}
catch (HttpRequestException exception)
{
if (exception.Message.Contains("401 (Unauthorized)"))
{
}
else if (exception.Message.Contains("403 (Forbidden)"))
{
}
}
catch (Exception)
{
}
return result;
}
public async Task<T> PostRequest(string apiUrl, T postObject)
{
T result = null;
try
{
using (var client = new HttpClient())
{
SetupClient(client, "POST", apiUrl, postObject);
var response = await client.PostAsync(apiUrl, postObject, new JsonMediaTypeFormatter()).ConfigureAwait(false); //<--- not javascript formatted
response.EnsureSuccessStatusCode();
await response.Content.ReadAsStringAsync().ContinueWith((Task<string> x) =>
{
if (x.IsFaulted)
throw x.Exception;
result = JsonConvert.DeserializeObject<T>(x.Result);
});
}
}
catch (HttpRequestException exception)
{
if (exception.Message.Contains("401 (Unauthorized)"))
{
}
else if (exception.Message.Contains("403 (Forbidden)"))
{
}
}
catch (Exception)
{
}
return result;
}
//...
}
My Web API Controller is defined like this:
[SecretAuthenticationFilter(SharedSecretName = "xxxxxxxxxxxxxxx", HmacSecret = true)]
public class ProfileController : ApiController
{
[HttpPost]
[ResponseType(typeof(Profile))]
public IHttpActionResult PostProfile(Profile Profile)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
GuidValue = Guid.NewGuid();
Resource res = new Resource();
res.ResourceId = GuidValue;
var data23 = Resourceservices.Insert(res);
Profile.ProfileId = data23.ResourceId;
_profileservices.Insert(Profile);
return CreatedAtRoute("DefaultApi", new { id = Profile.ProfileId }, Profile);
}
}
Here is some of what SecretAuthenticationFilter does:
//now try to read the content as string
string content = actionContext.Request.Content.ReadAsStringAsync().Result;
var contentMD5 = content == "" ? "" : Hashing.GetHashMD5OfString(content); //<-- Hashing the non-JavaScriptSerialized
var datePart = "";
var requestDate = DateTime.Now.AddDays(-2);
if (actionContext.Request.Headers.Date != null)
{
requestDate = actionContext.Request.Headers.Date.Value.UtcDateTime;
datePart = requestDate.ToString(CultureInfo.InvariantCulture);
}
var methodName = actionContext.Request.Method.Method;
var fullUri = actionContext.Request.RequestUri.ToString();
var messageRepresentation =
methodName + "\n" +
contentMD5 + "\n" +
datePart + "\n" +
fullUri;
var expectedValue = Hashing.GetHashHMACSHA256OfString(messageRepresentation, sharedSecretValue);
// Are the hmacs the same, and have we received it within +/- 5 mins (sending and
// receiving servers may not have exactly the same time)
if (messageSecretValue == expectedValue
&& requestDate > DateTime.UtcNow.AddMinutes(-5)
&& requestDate < DateTime.UtcNow.AddMinutes(5))
goodRequest = true;
Any idea why HMAC doesn't work for the POST?
EDIT:
When SecretAuthenticationFilter tries to compare the HMAC sent, with what it thinks the HMAC should be they don't match. The reason is the MD5Hash of the content doesn't match the MD5Hash of the received content. The RestClient hashes the content using a JavaScriptSerializer.Serialized version of the content, but then the PostRequest passes the object as JsonMediaTypeFormatted.
These two types don't get formatted the same. For instance, the JavaScriptSerializer give's us dates like this:
\"EnteredDate\":\"\/Date(1434642998639)\/\"
The passed content has dates like this:
\"EnteredDate\":\"2015-06-18T11:56:38.6390407-04:00\"
I guess I need the hash to use the same data that's passed, so the Filter on the other end can confirm it correctly. Thoughts?
EDIT:
Found the answer, I needed to change the SetupClient code from using this line:
var json = new JavaScriptSerializer().Serialize(content);
contentMD5 = Hashing.GetHashMD5OfString(json);
To using this:
var json = JsonConvert.SerializeObject(content);
contentMD5 = Hashing.GetHashMD5OfString(json);
Now the sent content (formatted via JSON) will match the hashed content.
I was not the person who wrote this code originally. :)
Found the answer, I needed to change the SetupClient code from using this line:
var json = new JavaScriptSerializer().Serialize(content);
contentMD5 = Hashing.GetHashMD5OfString(json);
To using this:
var json = JsonConvert.SerializeObject(content);
contentMD5 = Hashing.GetHashMD5OfString(json);
Now the content used for the hash will be formatted as JSON and will match the sent content (which is also formatted via JSON).

Categories

Resources