.NET MAUI Deserialising to list viewmodel from test API - c#

I have been using Xamarin Forms for a while but looking to start using .NET MAUI and I am having an issue when deserializing a response from my test API to a ViewModel List it is returning the list count but not binding the variables in the ViewModel if that makes sense.
I was using Newtonsoft which worked perfected but now I am on 6.0 I don't believe they support it so I am using the System.Text.Json library instead.
The code that worked was:
var response = client.GetAsync("WeatherForecast?format=json").Result
if(response.IsSuccessStatusCode)
{
var result = JsonConvert.DeserializeObject<List<WeatherViewModel>>(response.Content.ReadAsStringAsync().Result);
}
But now when I use:
var response = client.GetAsync("WeatherForecast?format=json").Result;
if (response.IsSuccessStatusCode)
{
var options = new JsonSerializerOptions { WriteIndented = true };
var responseData = JsonSerializer.Deserialize<List<WeatherViewModel>>(response.Content.ReadAsStringAsync().Result, options);
}
It gives me this in the debugger
But in the actual response.Content.ReadAsStringAsync the result is the following:
The ViewModel is structured like this:
public class WeatherViewModel
{
public DateTime Date { get; set; }
public Int32 TemperatureC { get; set; }
public Int32 TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public String Summary { get; set; }
}
Here is the JSON it brings back from the call:
[
{
"date": "2023-02-15T18:42:26.4694349+00:00",
"temperatureC": 39,
"temperatureF": 102,
"summary": "Hot"
},
{
"date": "2023-02-16T18:42:26.4694515+00:00",
"temperatureC": -3,
"temperatureF": 27,
"summary": "Cool"
},
{
"date": "2023-02-17T18:42:26.4694523+00:00",
"temperatureC": -9,
"temperatureF": 16,
"summary": "Bracing"
},
{
"date": "2023-02-18T18:42:26.469453+00:00",
"temperatureC": 9,
"temperatureF": 48,
"summary": "Cool"
},
{
"date": "2023-02-19T18:42:26.4694537+00:00",
"temperatureC": 5,
"temperatureF": 40,
"summary": "Chilly"
}
]
So the problem I am having is that I am getting the JSON ok from the API Get but when I deserialize it it is not populating into the ViewModel but it is creating 5 counts in the list. Could anybody shed some light on this for me. It would be a massive help.

it is always bad idea to post images instead of text, so I can't even see what is the problem, but I can see that you must be need to add PropertyNameCaseInsensitive, since your class property names and json property names differ. In Newtonsoft insensetivity is true by default.
var options = new JsonSerializerOptions { WriteIndented = true, PropertyNameCaseInsensitive = true};
JsonSerializer.Deserialize<...>(json, options)

Related

Message = "Unexpected character encountered while parsing value: [. Path '', line 1, position 1."

I want to show list of departments in my ASP.NET Core MVC view.
Notice that I have 3-tier layers (Data Access + API (to get data from database) + MVC (UI)).
Here is my Json Data that I got from database using a call to the API:
[
{
"id": 3,
"name": "Sales"
},
{
"id": 4,
"name": "PMO"
},
{
"id": 5,
"name": "Research And Development"
},
{
"id": 6,
"name": "Product Management"
},
{
"id": 7,
"name": "HR"
},
{
"id": 8,
"name": "Ava"
},
{
"id": 9,
"name": "IT"
}
]
Here is my C# code using HttpClient to get data from the API:
public async Task<T> GetRequest<T>(string uri)
{
try
{
var client = _httpClientFactory.CreateClient();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
using (HttpResponseMessage response = await client.GetAsync(uri))
{
if (response.StatusCode.ToString() == "OK")
{
_logger.LogInformation("Get Request Successed");
//response.EnsureSuccessStatusCode();
responseBody = await response.Content.ReadAsStringAsync();
}
return JsonConvert.DeserializeObject<T>(responseBody);
}
}
catch (Exception ex)
{
_logger.LogError("Failed");
return JsonConvert.DeserializeObject<T>(responseBody);
}
}
When I am trying to parse the data from json it returns an Error.
Unexpected character encountered while parsing value: [. Path '', line 1, position 1.
You can parse your string to a strongly typed Model and then de-serialize to it OR you can use dynamic and access the properties as shown below:
You can find a working example here
using System;
using Newtonsoft.Json;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var myJsonString=#"[{'id':3,'name':'Sales'},{'id':4,'name':'PMO'},{'id':5,'name':'Research And Development'},{'id':6,'name':'Product Management'},{'id':7,'name':'HR'},{'id':8,'name':'Ava'},{'id':9,'name':'IT'}]";
var result =JsonConvert.DeserializeObject<List<Root>>(myJsonString);
Console.WriteLine("Example using Model: \n");
foreach(var item in result)
{
Console.WriteLine(item.id);
Console.WriteLine(item.name);
}
Console.WriteLine("\n");
Console.WriteLine("Example using Dynamic: \n");
//Example using dynamic
var resultDynamic=JsonConvert.DeserializeObject<dynamic>(myJsonString);
foreach(var item in resultDynamic)
{
Console.WriteLine(item["id"]);
Console.WriteLine(item["name"]);
}
}
}
public class Root
{
public int id { get; set; }
public string name { get; set; }
}
Output:
Example using Model:
3
Sales
4
PMO
5
Research And Development
6
Product Management
7
HR
8
Ava
9
IT
Example using Dynamic:
3
Sales
4
PMO
5
Research And Development
6
Product Management
7
HR
8
Ava
9
IT

HTTP client method null exception

I have an API project and I need to develop a web project using the API I wrote some code but not able to find the exception and problem and not getting data from the link.
Here is my Service Code:
public async Task<IEnumerable<AgentReadDto>> GetAgent()
{
IEnumerable<AgentReadDto> agents = new List<AgentReadDto>();
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("https://localhost:44331/api/");
var response = client.GetAsync("Agent/GetAllAgent");
response.Wait();
var result = response.Result;
if (result.IsSuccessStatusCode)
{
var readTask =JsonConvert.DeserializeObject<IList<AgentReadDto>>(await result.Content.ReadAsStringAsync());
agents = readTask;
}
}
return agents;
}
And my controller code is look like this:
public IActionResult AgentLists()
{
var agentsList = _agentRespositoryWeb.GetAgent();
if (agentsList != null )
{
ViewBag.Message = "There was a problem retrieving agent from the database or no agents exists";
}
ViewBag.SuccessMessage = TempData["SuccessMessage"];
return View(agentsList);
}
My api return the value following:
{
"agentDetail": [
{
"usersId": 85,
"firstName": "Amit",
"lastName": "One",
"gender": "Male",
"informationTips": [
{
"video": "https://www.w3schools.com/html/movie.mp4"
},
{
"video": "https://www.w3schools.com/html/movie.mp4"
},
]
},
{
"usersId": 86,
"firstName": "Amit",
"lastName": "Two",
"gender": "Male",
"informationTips": [
{
"video": "https://www.w3schools.com/html/movie.mp4"
}
]
}
]
}
For exception I added image there is three image that take screen on the different steps:
Your model is set to IEnumerable<AgentReadDto>, but you've forgotten to await the call to GetAgent inside of the AgentLists action. This means there's a mismatch between what the view expects (IEnumerable<AgentReadDto>) and what it receives (Task<IEnumerable<AgentReadDto>>).
To fix this, convert AgentLists to an async method and then await the call to GetAgent. Here's a fixed version of the AgentLists action:
public async Task<IActionResult> AgentLists()
{
var agentsList = await _agentRespositoryWeb.GetAgent();
if (agentsList != null)
{
ViewBag.Message =
"There was a problem retrieving agent from the database or no agents exists";
}
ViewBag.SuccessMessage = TempData["SuccessMessage"];
return View(agentsList);
}
It looks like you also have a mismatch between the type you expect to be returned and the JSON actually being returned. The JSON represents an object with a list inside of it, but you're attempting to parse it as a simple list. To fix that, create a wrapper class that matches the structure of the response. For example, create the following class:
public class ApiResponse
{
public IEnumerable<AgentReadDto> AgentDetail { get; set; }
}
Update the deserialization logic to use this new type:
var apiResponse = JsonConvert.DeserializeObject<ApiResponse>(...);
var agentsLit = apiResponse.AgentDetail;

Patch API call in C# doesn't work, the same call works in swagger

I want to use an external API which has Swagger. In Swagger I am calling this url:
PATCH /rest/inventory/item/{id}
with parameters: X-Auth-Token, id and patchOperations which looks like this:
[
{
"op": "replace",
"path": "price",
"value": 6.2
}
]
And when I call this method with those parameters, it works. I get success code 200 and afterwards when I call the GET method I see that the price of the item has been updated to 6.2.
Now I want to do this in C#. I am already calling some GET methods from the same API successfully. This is my code for the PATCH method:
var model = new Dictionary<string, object>
{
{"op", "replace"},
{"path", "price"},
{"value", 6}
};
var blabla = await _httpProvider.PatchAsync($"https://{url}/server/rest/inventory/item/{id}", model, null, null, null, connection.Request.HeaderParameters);
public async Task<HttpResponseModel> PatchAsync<T>(string uri, T data, HttpClientHandler httpClientHandler = null, TimeSpan? timeout = null, string contentTypes = null, Dictionary<string, string> headerParameters = null)
{
using (var client = CreateHttpClient(httpClientHandler, timeout, contentTypes, headerParameters))
{
var requestContent = new StringContent(JsonConvert.SerializeObject(data));
var response = await client.PatchAsync(new Uri(uri), requestContent);
var result = new HttpResponseModel
{
Success = response.IsSuccessStatusCode,
ResponseContent = await response.Content.ReadAsStringAsync(),
ResponseTime = sw.Elapsed
};
return result;
}
}
Where is my mistake? I am getting error StatusCode: 500, ReasonPhrase: 'Server Error', Version: 1.1, Content: System.Net.Http.StreamContent
The mistake is that you're not pasting the same content, not quite anyway.
Your PATCH example is an array of a objects that has 3 properties, in your example there is only 1 element in the array, but it is still an array. Your C# is serialized into single object.
It's subtle but your JSON that you are sending is actually:
{
"op": "replace",
"path": "price",
"value": 6
}
So instead you need to send your dictionary or other object inside an array:
var model = new List<object> {
{
new Dictionary<string, object>
{
{ "op", "replace"},
{"path", "price"},
{"value", 6}
}
};
Ideally, in c# you would create a class to represent this DTO (Data Transfer Object), it can work with anonymous types or with dictionaries (a Dictionary<string,object> serializes into a single JSON object) but the code gets harder to manage over time.
public class DTO
{
public string op { get; set; }
public string path { get; set; }
public object value { get; set; }
}
...
var model = new List<DTO>
{
new DTO {
op = "replace",
path = "price",
value = 6
}
};

WEB API 1 - Pass JSON ARRAY from .NET CLIENT

I am trying to create one WEB API controller (Service method) which accept an array of a class object.
And then also a .NET client which makes a call to this API method and pass JSON string (array of class object). Issue is I am not able to receive json array contents on server side. Seems some serialization/de-serialization error but I am not able to spot. Please see sample code as below:
C# class as below:
public class UserData
{
public int ID { get; set; }
public DateTime DATETIME { get; set; }
public int SEQUENCE { get; set; }
}
And then WEB API method (API Controller as below)
[HttpPost()]
public HttpResponseMessage Post([FromBody()]
IEnumerable<#UserData> RequestBody)
{
}
Json array as below
[
{
"ID": 1,
"DATE": "2014-01-01",
"SEQUENCE": 533
},
{
"ID": 2,
"DATE": "2015-01-01",
"SEQUENCE": 3233
},
{
"ID": 3,
"DATE": "2015-01-01",
"SEQUENCE": 233
}
]
And the .NET Client as below:
public void CallService(string jsonString)
{
try {
var client = new RestClient(GetBaseURLService());
var requestRest = new RestRequest("event ", Method.POST);
var RequestBody = TextBoxCreateEventJson.Text;
requestRest.AddBody(jsonString);
requestRest.RequestFormat = DataFormat.Json;
var res = client.Execute(requestRest);
} catch (Exception ex) {
}
}
And then I get null/nothing in the RequestBody.
I know something I need to do before a call to requestRest.AddBody(jsonString);
But not sure what?
In System.Web.Helpers namespace, there is a Json class which you can use in order to encode or decode. For your case,
string jsonString = Json.Encode(your array as an argument)
Use jsonString in the body of your request.

Finding specific values in JSON.NET with C#

I've been making a Minecraft launcher. I have a long JSON file that contains all the libraries required to launch one version. A sample of this code:
{
"id": "1.6.2",
"time": "2013-08-06T14:00:00+03:00",
"releaseTime": "2013-07-05T16:09:02+03:00",
"type": "release",
"minecraftArguments": "--username ${auth_player_name} --session ${auth_session} --version ${version_name} --gameDir ${game_directory} --assetsDir ${game_assets}",
"libraries": [
{
"name": "net.sf.jopt-simple:jopt-simple:4.5"
},
{
"name": "com.paulscode:codecjorbis:20101023"
},
So you can see that there is an array called libraries. I can query the values in "name" using a foreach quite fine, but sometimes this occurs in the json:
{
"name": "org.lwjgl.lwjgl:lwjgl:2.9.0",
"rules": [
{
"action": "allow"
},
{
"action": "disallow",
"os": {
"name": "osx",
"version": "^10\\.5\\.\\d$"
}
}
]
},
So as you can see, there is an array inside called rules. I need to find the value of name inside os inside rules. Anything I've tried came up with an exception and wouldn't work. This is my code at the moment for parsing the code:
foreach (JToken lib in profilejsono["libraries"])
{
if ((lib["rules"][1]["os"].ToString() == "osx") || (lib["rules"][1]["os"].ToString() == "linux"))
{
availableLibs.Add(lib["name"].ToString());
}
}
The point of the code is not to add the library if it's for OSX or Linux (I'll add that functionality later). How do I do this?
One possible solution to your problem is to generalize the check for the OS name (and make it independant of the rules node depth). I suppose you have some library objects, because you need to deserialize the JSON input string to some object. The following code gives you the library names only for library nodes that have rule nodes with os nodes with a specified name:
JSON Test-Input file lib1.json:
{
"name": "lib1",
"rules": [
{
"action": "allow"
},
{
"action": "disallow",
"os": {
"name": "windows",
"version": "^10\\.5\\.\\d$"
}
}
]
}
JSON Test-Input file lib2.json:
{
"name": "lib2",
"rules": [
{
"action": "allow"
},
{
"action": "disallow",
"os": {
"name": "osx",
"version": "^10\\.5\\.\\d$"
}
}
]
}
JSON Test-Input file lib3.json:
{
"name": "lib3",
"rules": [
{
"action": "allow"
},
{
"action": "disallow",
"os": {
"name": "linux",
"version": "^10\\.5\\.\\d$"
}
}
]
}
JSON Test-Input file lib4.json:
{
"name": "lib4",
"rules": [
{
"action": "allow"
},
{
"action": "disallow"
}
]
}
JSON helper objects describing the library node, needed to deserialize the JSON inputs:
// helper classes describing the library object (for the JSON deserialization)
public class Library
{
public String Name { get; set; }
public Rules[] Rules { get; set; }
}
public class Rules
{
public String Action { get; set; }
public Os Os { get; set; }
}
public class Os
{
public String Name { get; set; }
public String Version { get; set; }
}
Code for fetching only matching libraries:
var jsonInput = #"d:\temp\json.net\lib{0}.json";
try
{
// load libraries / deserialize json
var libraries = new List<Library>();
Enumerable.Range(1, 4).ToList().ForEach(index =>
{
var json = File.ReadAllText(String.Format(jsonInput, index));
libraries.Add(JsonConvert.DeserializeObject<Library>(json));
});
// OS names to check if present in the current rules
var osNames = new List<String> { "osx", "linux" };
// check each library
foreach (var library in libraries)
{
// do we have rules?
if (library.Rules.Length > 0)
{
// get all non-empty OS nodes
var existingOsNodes = library.Rules.Where (r => r.Os != null).Select (r => r.Os).ToList();
// check for allowed OS name
var osIsPresent = existingOsNodes.Where (node => osNames.Contains(node.Name.ToLower())).Select (node => node.Name);
if (osIsPresent.Any())
{
Console.WriteLine(library.Name);
}
}
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
The output for the four given input files is:
lib2
lib3

Categories