Deserialize json response - c#

I'm using an API that among other things have these two responses:
{
"enrollDeviceErrorResponse": {
"errorCode": "GRX-1056",
"errorMessage": "DEP Reseller ID missing.
Enter a valid DEP Reseller ID and resubmit your request."
}
}
or
{
"enrollDeviceErrorResponse": [
{
"errorCode": "GRX-1056",
"errorMessage": "DEP Reseller ID missing.
Enter a valid DEP Reseller ID and resubmit your request."
},
{
"errorCode": "DEP-ERR-3003",
"errorMessage": "Order information missing. T
he transaction needs to have one or more valid orders.
Enter valid orders and resubmit your request."
},
{
"errorCode": "DEP-ERR-3001",
"errorMessage": "Transaction ID missing.
Enter a valid transaction ID and resubmit your request."
}
]
}
I've created some classes that can deserialize into these responses:
public class EnrollDeviceErrorRoot
{
public EnrollDeviceErrorRoot()
{
this.EnrollDeviceErrorResponse = new EnrollDeviceErrorResponse();
}
public EnrollDeviceErrorResponse EnrollDeviceErrorResponse { get; set; }
}
public class EnrollDeviceErrorRoot
{
public EnrollDeviceErrorRoot()
{
this.EnrollDeviceErrorResponse = new EnrollDeviceErrorResponse();
}
public EnrollDeviceErrorResponse EnrollDeviceErrorResponse { get; set; }
}
public class EnrollDeviceErrorResponse
{
public string ErrorCode { get; set; }
public string ErrorMessage { get; set; }
}
I am having a hard time coming up with a nice way to determine which one of my classes I should use depending on the response. I currently have this failing code:
var multipleErrors = JsonConvert.DeserializeObject<EnrollDeviceErrorsRoot>(response);
if (multipleErrors.EnrollDeviceErrorResponse != null && multipleErrors.EnrollDeviceErrorResponse.Any())
{
this.StatusCode = multipleErrors.EnrollDeviceErrorResponse.Select(x => x.ErrorCode).Aggregate((a, b) => a + ", " + b);
this.StatusMessage = multipleErrors.EnrollDeviceErrorResponse.Select(x => x.ErrorMessage).Aggregate((a, b) => a + Environment.NewLine + b);
this.HasErrors = true;
return;
}
var singleError = JsonConvert.DeserializeObject<EnrollDeviceErrorRoot>(response);
if (!string.IsNullOrEmpty(singleError.EnrollDeviceErrorResponse.ErrorCode))
{
this.StatusCode = singleError.EnrollDeviceErrorResponse.ErrorCode;
this.StatusMessage = singleError.EnrollDeviceErrorResponse.ErrorMessage;
this.HasErrors = true;
return;
}
Error from code above:
Newtonsoft.Json.JsonSerializationException : Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[Atea.Dep.Core.Service.Models.EnrollDeviceErrorResponse]'
because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer,
not a collection type like an array or List<T>) that can be deserialized from a JSON object.
JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.
How are other people handling this deserializations from responses when they don't know what they are getting back from the server? I could of course just look at the raw response and determine that way but are there other cleaner ways to do this? I Can't change the API, I have no idea why there just couldn't be one response with a list with one or more errors.

You can use
JObject.Parse(response)["enrollDeviceErrorResponse"].Type
to determine the type of the response. In 1st case it will be obect, in 2nd - array. Then it will be easy to continue with proper deserialization.

You can use something like this:
var output;
dynamic jObject = JsonConvert.DeserializeObject (outputAPI);
bool isArray = jObj.enrollDeviceErrorResponse.Type == JTokenType.Array;
bool isObject = jObj.enrollDeviceErrorResponse.Type == JTokenType.Object;
if(isObject)
output = JsonConvert.DeserializeObject<EnrollDeviceErrorResponse>(outputAPI);
//else you will use the other class to deserialize the object

Related

Converting a JSON into own type results in null objects

from my rest call, I am receiving this JSON:
{
"livemode": true,
"error": {
"type": "unauthorized",
"message": "You did not provide a valid API key."
}
}
I need to fetch type and message into my type:
public class TestObject
{
string type { get; set; }
string message { get; set; }
}
But this returns null objects:
HttpClient client = new HttpClient();
Uri uri = new Uri("https://api.onlinebetaalplatform.nl/v1");
HttpResponseMessage response = await client.GetAsync(uri);
string content = await response.Content.ReadAsStringAsync();
JObject json = JObject.Parse(content);
TestObject album = json.ToObject<TestObject>();
1.) I understand that the type and message attributes are "nested". How do I access them?
2.) Even if I call my type livemode and error, the objects still return null.
Can you help me out a little?
Thank you :)
There seems to be one set of curly brackets to many. I am pretty sure that the api you are querying is not returning the first and the last curly bracket. Continue on after that has been taken care of.
In order to fetch the data, add these class definitions
public class Error
{
public string type { get; set; }
public string message { get; set; }
}
public class Root
{
public bool livemode { get; set; }
public Error error { get; set; }
}
and change
TestObject album = json.ToObject<TestObject>();
To
Root album = json.ToObject<Root>();
As some of the comments to your question mentioned, you are currently trying to convert the JSON string to the nested Error object instead of the root object, where the Error object is located.
In the future, there are tools that can generate C# classes from JSON. I used https://json2csharp.com/ this time around to do so.
EDIT:
I just found out that Visual Studio actually has an in-built JSON to Class feature!

POST 2 Guid parameters to API with Angular

I Have made an API to follow a user. The method accepts 2 parameters which are two guids. The method:
// Follow user
[HttpPost]
public async Task<ActionResult<Guid>> FollowUser([FromBody] Guid user_gd, Guid user2_gd)
{
if (ModelState.ErrorCount > 0)
{
return BadRequest();
}
var followedUser = await _user.FollowUser(user_gd, user2_gd);
return Ok(followedUser);
}
The manager in API:
public async Task<bool> FollowUser(Guid user_gd, Guid user2_gd)
{
var followUserQuery =
#"
insert into userbind(gd, user_gd, followed_user_gd, date_followed)
values(#_gd, #_user_gd, #_followed_user_gd, #_date_followed)
";
await PostQuery(followUserQuery, new
{
_gd = GenerateGd(),
_user_gd = user_gd,
_followed_user_gd = user2_gd,
_date_followed = DateTime.Now
});
return true;
}
The API request in Angular (service):
followUser(followed_user, user_gd): Observable<any> {
try {
return this._http.post<any>(this._apiUrl + "FollowUser", { "user2_gd": followed_user, "user_gd": user_gd }, this.httpOptions);
} catch (e) {
console.log("POST error: ", e);
}
}
The component:
followUser(gd) {
console.log(gd);
this._userService.followUser(gd, localStorage.getItem("gd")).subscribe(
res => {
console.log(res);
}
)
}
The variables and everything works right now but I am getting this error everytime:\
"Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Guid' because the type requires a JSON primitive value (e.g. string, number, boolean, null) to deserialize correctly.\r\nTo fix this error either change the JSON to a JSON primitive value (e.g. string, number, boolean, null) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.\r\nPath 'user2_gd', line 2, position 15."
Does someone know how to fix this or expierences the same problem? Please reach out.
Try creating class with required fields and accepting it in your FollowUser action as a parameter:
public class FollowUserParams
{
public Guid user_gd { get; set; }
public Guid user2_gd { get; set; }
}
// Follow user
[HttpPost]
public async Task<ActionResult<Guid>> FollowUser([FromBody] FollowUserParams p)
{
... use p
}
Also see Andrew Lock's post on model binding in ASP.

Can't figure out how to store data from API call [duplicate]

This question already has answers here:
Converting JSON to Object fails - Cannot deserialize the current JSON object into System.Collections.Generic.List
(4 answers)
Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1
(6 answers)
Cannot deserialize the current JSON object (e.g. {"name":"value"})
(6 answers)
JSON.Net - cannot deserialize the current json object (e.g. {"name":"value"}) into type 'system.collections.generic.list`1
(2 answers)
Closed 2 years ago.
This is my first time asking a question. I've been trying to get this working for too long. I've looked over some similar posts but just can't quite crack it.
I'm trying to make an API call in c# using flurl. I can make the call, but can't figure out how to store the data.
Here is an example of the response from the call made using powershell:
StatusCode : 200
StatusDescription : OK
Content : {
"result": [
{
"apiUserName": "admin#somedomain.com",
"apiPassword": "dk65dss5s5HH",
"id": "d38a1ea4-46d1-46b7-87a2-46c09da663eb",
"name": "catHotel",
"applUrl": "https://catshotel.somedomain.com/manager/",
"apiUrl": "https://catshotel.somedomain.com/API/",
"stateId": 2,
"databaseName": null,
"databaseServerName": null
},
{
"apiUserName": "admin#somedomain.com",
"apiPassword": "dhh7k33kDDh5g",
"id": "00d5e97b-5ea6-47dd-b920-8678f949c51f",
"name": "possumLodge",
"applUrl": "https://possumlodge.somedomain.com/manager/",
"apiUrl": "https://possumlodge.somedomain.com/API/",
"stateId": 1,
"databaseName": "customer-datab",
"databaseServerName": "customersDBserv.somedomain.com"
}
],
"targetUrl": null,
"success": true,
"error": null,
"unAuthorizedRequest": false,
"__abp": true
}
RawContent : HTTP/1.1 200 OK
Cache-Control: no-store, must-revalidate, no-cache, max-age=0
Request-Context: appId=cid-v1:8cgg58f2-5212-4685-b45f-149104d7a5be
Access-Control-Expose-Headers: Request-Context
X-Fr…
Headers : {[Cache-Control, System.String[]], [Request-Context, System.String[]], [Access-Control-Expose-Headers, System.String[]], [X-Frame-Options, System.String[]]…}
Images : {}
InputFields : {}
Links : {}
RawContentLength : 23638
RelationLink : {}
In c#, I've made a class: -
class Facilities
{
public string Name { get; set; }
}
And then I make the call: -
List<facilities> facilities = await url.WithHeaders(new { Accept = "Application/json", Authorization = token }).GetJsonAsync<List<facilities>>();
But I get errors such as:-
"Message "Cannot deserialize the current JSON object (e.g.
{"name":"value"}) into type
'System.Collections.Generic.List`1[ConsoleApp2.facilities]' because
the type requires a JSON array (e.g. [1,2,3]) to deserialize
correctly.\r\nTo fix this error either change the JSON to a JSON array
(e.g. [1,2,3]) or change the deserialized type so that it is a normal
.NET type (e.g. not a primitive type like integer, not a collection
type like an array or List) that can be deserialized from a JSON
object. JsonObjectAttribute can also be added to the type to force it
to deserialize from a JSON object.\r\nPath 'result', line 1, position
10." string"
I can't figure out how to store each 'facility' into an array of facilities :(
I'm Not an Expert here and Can't say i know about flurl, so i looked here https://jonathancrozier.com/blog/calling-all-apis-how-to-use-flurl-with-c-sharp
and looked into the error message and as the message say, your strongly-typed model need to match the Json object, so that the deserialization mechanism in flurl can deserialize the Json objects that match your strongly-typed model properties, meaning your class Facilities should look something like this.
class Facilities
{
public string apiUserName { get; set; }
public string apiPassword { get; set; }
public int id { get; set; }
public string name { get; set; }
public string applUrl { get; set; }
public string apiUrl { get; set; }
public int stateId { get; set; }
public string databaseName { get; set; }
public string databaseServerName { get; set; }
}
And try using
var facilities = await url.WithHeaders....
so you can figure out what can of object you are getting.
Note: I mimicked your request code using the Package Flurl.Http v2.4.2, with this code
using Flurl;
using Flurl.Http;
var token = "SomeToken1234";
Url url = "https://jsonplaceholder.typicode.com/todos";
List<Todo> todos = await url.WithHeaders(new { Accept = "application/json", Authorization = token }).GetJsonAsync<List<Todo>>();
this here work, you can test it, maybe it's something regard the version you use.
You can use a System.Dynamic namespace
Then deserialize it like so:
var responseContent = response.Content;
var responseString = responseContent.ReadAsStringAsync().Result;
dynamic projects = JArray.Parse(responseString) as JArray;
the controller action needs to return VIEW(project)
lastly, your View page needs to use #model dynamic

C# Deserialize List<someobject> restfully with ServiceStack

I'm attempting to receive a POSTed List of POCO but I'm unable to deserialize either via the Swagger API or via Postman when submitting over the wire. I've serialized the object back out to confirm how it supposed to be serialized, but when returning that back the body of the form, the request object parameter is null.
public class NameValuePair
{
public string Name { get; set; }
public string Value { get; set; }
}
public class NameValueList
{
private List<NameValuePair> valuesToProcess = null;
public List<NameValuePair> ValuesToProcess { get { return valuesToProcess; } set { valuesToProcess = value; } }
public NameValueList()
{
valuesToProcess = new List<NameValuePair>();
}
}
[Api("A description of what the API does")]
[Tag("Enriching Requests")]
[ApiResponse(HttpStatusCode.BadRequest, "Your request was not understood")]
[ApiResponse(HttpStatusCode.InternalServerError, "Oops, something broke")]
[Route("/EnrichValueList", "POST", Summary = "Summary of the Web Get method", Notes = "Longer information about what this service does")]
public class EnrichValueList : IPost, IReturn<EnrichValueListResponse>
{
[ApiMember(Name = "NameValues", Description = "A string that represents a value with which to understand more information about", ParameterType = "body", DataType = "NameValueList", IsRequired = true)]
public NameValueList NameValues
{
get;
set;
}
[ApiMember(Name = "AssociatedActivities", Description = "A comma seperated string that links Enrichment Activities with this value", ParameterType = "body", DataType = "string", IsRequired = false)]
public string AssociatedActivities
{
get;
set;
}
}
The request.NameValues in this case is null (no error is thrown):
public async Task<EnrichValueListResponse> Post(EnrichValueList request)
{
EnrichValueListResponse enrichValueListResponse = new EnrichValueListResponse();
return enrichValueListResponse;
}
I've already got other methods that receive a string of stringyfied object and then use the JsonSerializer.DeserializeFromString method from ServiceStack.Text so completely fine with that approach. I was attempting to use a more strongly typed object in the original request (which may not be possible or I'm doing it wrong).
Param: NameValues, Value {"valuesToProcess":[{"name":"bob","value":"Not Bob"}]}
and trying about every other permutation I can think of. Interestingly, when changing to plain string parameters and posting, the inbuilt swagger API returns a deserialization error, but Postman is fine.
Swagger Top Half
Swagger Bottom Half
Response Body as text
{
"responseStatus": {
"errorCode": "SerializationException",
"message": "Type definitions should start with a '{', expecting serialized type 'EnrichValueList', got string starting with: \"two\"",
"stackTrace": " at ServiceStack.Text.Common.DeserializeTypeRefJson.StringToType(ReadOnlySpan`1 strType, TypeConfig typeConfig, EmptyCtorDelegate ctorFn, KeyValuePair`2[] typeAccessors)\r\n at ServiceStack.Text.Common.DeserializeType`1.StringToTypeContext.DeserializeJson(ReadOnlySpan`1 value)\r\n at ServiceStack.Text.Json.JsonReader.<>c__DisplayClass3_0.<GetParseSpanFn>b__0(ReadOnlySpan`1 v)\r\n at ServiceStack.Text.JsonSerializer.DeserializeFromSpan(Type type, ReadOnlySpan`1 value)\r\n at ServiceStack.Memory.NetCoreMemory.Deserialize(MemoryStream memoryStream, Boolean fromPool, Type type, DeserializeStringSpanDelegate deserializer)\r\n at ServiceStack.Memory.NetCoreMemory.DeserializeAsync(Stream stream, Type type, DeserializeStringSpanDelegate deserializer)\r\n at ServiceStack.Host.RestHandler.CreateRequestAsync(IRequest httpReq, IRestPath restPath, Dictionary`2 requestParams)\r\n at ServiceStack.Host.RestHandler.CreateRequestAsync(IRequest httpReq, IRestPath restPath)\r\n at ServiceStack.Host.RestHandler.ProcessRequestAsync(IRequest req, IResponse httpRes, String operationName)"
}
}
Postman
Update
Following #mythz advice, I removed both the ParameterType and the DataType from the decorator and I was able to exhibit some slightly different behaviour.
Using the example classes:
public class NameValues
{
public string Name { get; set; }
public List<string> Values { get; set; }
public NameValues()
{
Values = new List<string>();
Name = string.Empty;
}
}
public class NameValuesList
{
public List<NameValues> ValuesToProcess { get; set; }
public NameValuesList()
{
ValuesToProcess = new List<NameValues>();
}
}
and setting the DTO parameter to this
[ApiMember(Name = "NameValues", Description = "A string that represents a value with which to understand more information about", IsRequired = true)]
public NameValuesList NameValues
{
get;
set;
}
causes the same deserialization error when I pass in a JSON structure that should deserialize. However, if I pass in some deformed string, it throws no error and just runs on through to the IPost handler method with a null for the property.
If I change the API parameter back to a string and use this example to show serialization and deserialization using the ServiceStack.Text library, works like a charm through both Swagger and Postman.
public async Task<EnrichValueListResponse> Post(EnrichValueList request)
{
EnrichValueListResponse enrichValueListResponse = new EnrichValueListResponse();
// Create a dummy object to serialize as JSON to return as an example back to the caller
NameValuesList stuff = new NameValuesList();
stuff.ValuesToProcess.Add(new NameValues { Name = "first", Values = { "second", "third" } });
enrichValueListResponse.BatchId = await Task.Run(() => stuff.ToJson().IndentJson());
// Deserialize the inbound string
NameValuesList betterStuff = JsonSerializer.DeserializeFromString<NameValuesList>(request.NameValues);
return enrichValueListResponse;
}
JSON submitted (although I did try many different variations of encoding and structure).
{
"valuesToProcess": [
{
"name": "first",
"values": [
"second",
"third"
]
}
]
}
I've got debug mode set, but not seeing any exceptions thrown to the SeriLog db during parsing. For now, I'll run with string parameter and derserialize after the fact. I can do some pre-checking in the Fluent Validator

Deserialization Issue by Newtonsoft

I want to deserilize this Json to Model, this is the
Json: [{"dimensions": [ "www "], "metrics ": [{ "values ": [ "2 " ]},{ "values ": [ "0 "]}]}]
and i create this model to deserialize json to this model:
public class ResultModel
{
public List<string> dimensions { get; set; }
public List<Metric> metrics { get; set; }
}
and
public class Metric
{
public List<string> values { get; set; }
}
and using Newtonsoft for this:
var model = Newtonsoft.Json.JsonConvert.DeserializeObject<ResultModel>(json);
but it doesnot work and give me this error:
'Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'ResultModel' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly'.
What is the problem?
It's easy to miss that your JSON is really array, even if it contains just one object. So you have to deserialize it to array:
var model = Newtonsoft.Json.JsonConvert.DeserializeObject<ResultModel[]>(json);
If you're sure that there will be at most one model, you can do:
var model = JsonConvert.DeserializeObject<IEnumerable<ResultModel>>(json).FirstOrDefault();
Otherwise, you should deserialize it to a collection of models, as suggested in the comments:
// models is an IEnumerable<ResultModel>
var models = JsonConvert.DeserializeObject<IEnumerable<ResultModel>>(json);

Categories