Newtonsoft.JSON null values when deserializing - c#

I'm using .NET CORE 3.1 and Newtonsoft.Json to deserialize a JSON response from the API.
this is how the JSON response is structured from the API:
{
"PageSize": 200,
"PageCount": 1,
"RecordCount": 13,
"PageNumber": 1,
"Result": "OK",
"organization":[
{
"5LevelOrganization": {
"organizationLevel1Code": "xxxxxxxx",
"organizationLevel1Name": "Corporate Human Resources",
"organizationLevel2Code": "xxxxxxxx",
"organizationLevel2Name": "BHR Downstream & Midstream",
"organizationLevel3Code": "xxxxxxxx",
"organizationLevel3Name": "Chemicals",
"organizationLevel4Code": "",
"organizationLevel4Name": "",
"organizationLevel5Code": "xxxxxxxx",
"organizationLevel5Name": "Chemicals"
}
},
{
"5LevelOrganization": {
"organizationLevel1Code": "xxxxxxxx",
"organizationLevel1Name": "Corporate Human Resources",
"organizationLevel2Code": "xxxxxxxx",
"organizationLevel2Name": "BHR Downstream & Midstream",
"organizationLevel3Code": "xxxxxxxx",
"organizationLevel3Name": "Chemicals",
"organizationLevel4Code": "xxxxxxxx",
"organizationLevel4Name": "Americas Oronite Manufacturing HR",
"organizationLevel5Code": "xxxxxxxx",
"organizationLevel5Name": "Americas Oronite HR Managed"
}
}
]
}
This is how I structured the c# class with regards to the response:
public class _5LevelOrganization
{
[JsonPropertyName("organizationLevel1Code")]
public string OrganizationLevel1Code { get; set; }
[JsonPropertyName("organizationLevel1Name")]
public string OrganizationLevel1Name { get; set; }
[JsonPropertyName("organizationLevel2Code")]
public string OrganizationLevel2Code { get; set; }
[JsonPropertyName("organizationLevel2Name")]
public string OrganizationLevel2Name { get; set; }
[JsonPropertyName("organizationLevel3Code")]
public string OrganizationLevel3Code { get; set; }
[JsonPropertyName("organizationLevel3Name")]
public string OrganizationLevel3Name { get; set; }
[JsonPropertyName("organizationLevel4Code")]
public string OrganizationLevel4Code { get; set; }
[JsonPropertyName("organizationLevel4Name")]
public string OrganizationLevel4Name { get; set; }
[JsonPropertyName("organizationLevel5Code")]
public string OrganizationLevel5Code { get; set; }
[JsonPropertyName("organizationLevel5Name")]
public string OrganizationLevel5Name { get; set; }
}
public class Organization
{
[JsonPropertyName("5LevelOrganization")]
public _5LevelOrganization _5LevelOrganization { get; set; }
}
public class FiveLevel
{
[JsonPropertyName("PageSize")]
public int PageSize { get; set; }
[JsonPropertyName("PageCount")]
public int PageCount { get; set; }
[JsonPropertyName("RecordCount")]
public int RecordCount { get; set; }
[JsonPropertyName("PageNumber")]
public int PageNumber { get; set; }
[JsonPropertyName("Result")]
public string Result { get; set; }
[JsonPropertyName("organization")]
public List<Organization> Organization { get; set; }
}
The problem is when I try to deserialize the response JSON it always leads to having null values for 5LevelOrganization:
var fiveLevelResult = _5levelresponse.Content.ReadAsStringAsync().Result;
FiveLevel fivelevel = JsonConvert.DeserializeObject<FiveLevel>(fiveLevelResult);
Image for NULL 5LevelOrganization
Initially I thought the issue was with the JSON response property which starts with a number (5LevelOrganization) so I added the JsonPropertyAttribute but it still won't deserialize properly resulting to NULL. The response from the API is as expected.
I'm just wondering where I did wrong? Any help or information is greatly appreciated.

[JsonPropertyName] is a System.Text.Json attribute but you are trying to use a Newtonsoft.Json deserialiazer.
You have to change attribute to [JsonProperty] or to use a System.Text.Json deserializer
using System.Text.Json;
FiveLevel fivelevel = JsonSerializer.Deserialize<FiveLevel>(fiveLevelResult);

As what Llama said I was using the wrong attribute for the Serializer that I'm using when I switched [JsonPropertyName] to [JsonProperty] from the JSON.NET library the results is as expected.

Related

Newtonsoft.Json Deserialization only sets default values, incorrect data

I am loading JSON data in my Unity project via Newtonsoft JsonConvert.Deserialize. This works in Editor and the json is valid. However, as soon as I make a Windows build, the serialized data is empty / filled with default values instead.
This is also shown when I serialize the deserialized data and output it:
Json string read:
{
"galleries": [{
"id": "Acker",
"title": "Auf dem Acker",
"slides": [],
"path": "/ChangeableContentData/Auf dem Acker",
"showAllInFolder": true,
"foldersRecursive": false,
"enableSlideshow": false,
"slideshowSlideDuration": 5.0
}],
"cardMenus": [],
"urls": []
}
reserialized:
{
"galleries": [{
"id": null,
"title": "",
"slides": null,
"path": "",
"showAllInFolder": true,
"foldersRecursive": true,
"enableSlideshow": false,
"slideshowSlideDuration": 5.0
}],
"cardMenus": []
}
Here is the relevant C# class:
[System.Serializable]
[Preserve]
public partial class UIContentData
{
[JsonProperty("galleries")]
public List<UIContentGalleryData> galleries { get; set; }
[JsonProperty("cardMenus")]
public List<UIContentCardMenuData> cardMenus { get; set; }
[JsonProperty("urls")]
public List<UIContentUrlData> urls { get; set; }
public UIContentData() { }
}
[System.Serializable]
[Preserve]
public partial class UIContentGalleryData
{
public string id { get; set; }
[JsonProperty("title")]
public string title { get; set; } = ""; // optional
[JsonProperty("slides")]
public List<UIContentGallerySlide> slides { get; set; }
[JsonProperty("path")]
public string baseFolderPath { get; set; } = "";
[JsonProperty("showAllInFolder")]
public bool showAllInFolder { get; set; } = true; // optional
[JsonProperty("foldersRecursive")]
public bool foldersRecursive { get; set; } = true; // optional
[JsonProperty("enableSlideshow")]
public bool enableSlideshow { get; set; } = false; // optional
[JsonProperty("slideshowSlideDuration")]
public float slideshowSlideDuration { get; set; } = 5f; // optional
public UIContentGalleryData() { }
}
Edit for clarity: I am using UIContentData object to deserialize.
JsonConvert.DeserializeObject<UIContentData>(json);
Any help is much appreciated!
You are using the wrong class,should create one more. This code was tested and working properly
var data=JsonConvert.DeserializeObject<Root>(json);
public class Root
{
public List<UIContentGalleryData> galleries { get; set; }
public List<object> cardMenus { get; set; }
public List<object> urls { get; set; }
}
and remove slides property from UIContentGallerySlide
[JsonProperty("slides")]
public List<UIContentGallerySlide> slides { get; set; }
Found the issue. Unity was stripping the class definitions I believe. Adding a link.xml with preserve="all" for the assembly containing them solved the problem!

Cannot serialize object from JSON using Unity's JsonUtilty when the object contains more objects

So I have an API set up that returns a JSON. The response looks as follows:
{
"success": true,
"results": 1,
"data": [
{
"location": {
"type": "Point",
"coordinates": [
-83.338322,
42.705647
],
"formattedAddress": "Some adress",
"city": "SomeCity",
"state": "STATE",
"zipcode": "000000",
"country": "US"
},
"_id": "630ce2c5a963f97ddae7387f",
"title": "Node Developer",
"description": "Must be a full-stack developer, able to implement everything in a MEAN or MERN stack paradigm (MongoDB, Express, Angular and/or React, and Node.js).",
"email": "employer#gmail.com",
"address": "someADress",
"company": "Company Ltd",
"industry": [
"Information Technology"
],
"jobType": "Permanent",
"minEducation": "Bachelors",
"positions": 2,
"experience": "No Experience",
"salary": 155000,
"lastDate": "2022-09-05T15:41:44.912Z",
"user": "630cdf34b2bc6356a75ec29c",
"postingDate": "2022-08-29T16:01:09.707Z",
"slug": "node-developer"
}
] }
And I am trying to serialize it as an object in C# using the following code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
public class test : MonoBehaviour
{
public string domain = "http://localhost:3000/api/v1";
public string request = "/jobs";
private bool coroutineRunning = false;
Request req;
private void Start()
{
req = new Request();
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space) && !coroutineRunning)
{
StartCoroutine(GetRequest(domain + request));
}
}
IEnumerator GetRequest(string uri)
{
coroutineRunning = true;
UnityWebRequest request = UnityWebRequest.Get(uri);
yield return request.SendWebRequest();
if (request.result == UnityWebRequest.Result.Success)
{
string jsonText = request.downloadHandler.text;
Debug.Log(jsonText);
SetUpObject(jsonText);
}
else
{
Debug.LogError(request.result);
}
coroutineRunning = false;
}
void SetUpObject(string json)
{
req = JsonUtility.FromJson<Request>(json);
Debug.Log(JsonUtility.ToJson(req, true));
Debug.Log(req);
}
}
The Request class:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[Serializable]
public class Data
{
public Location location { get; set; }
public string _id { get; set; }
public string title { get; set; }
public string description { get; set; }
public string email { get; set; }
public string address { get; set; }
public string company { get; set; }
public string[] industry { get; set; }
public string jobType { get; set; }
public string minEducation { get; set; }
public int positions { get; set; }
public string experience { get; set; }
public int salary { get; set; }
public DateTime lastDate { get; set; }
public string user { get; set; }
public DateTime postingDate { get; set; }
public string slug { get; set; }
}
[Serializable]
public class Location
{
public string type { get; set; }
public double[] coordinates { get; set; }
public string formattedAddress { get; set; }
public string city { get; set; }
public string state { get; set; }
public string zipcode { get; set; }
public string country { get; set; }
}
[Serializable]
public class Request
{
public bool success { get; set; }
public int results { get; set; }
public Data[] data { get; set; }
}
The problem is that I can't figure out how I should create a class that can get serialized with this JSON. This API is from a course I took on creating API's and won't be used in a project, I am just trying to figure out how to properly use serialization with complex JSONs such as this one.
The request returns a string that is the JSON written above. I used that JSON format and JSON2CSHARP to generate the Request class (i know it should be called the result class). Now when I copied the generated class I swapped every List<T> with an array of that type. I also think that using Datetime instead of string and other type mismatches could be the source of the problem, but even if they were the JsonUtilty would've still populated the Success and Results fields of the Request class and left the Data as an empty array
From the Unity documentation, I see the following (emphasis added):
Fields of the object must have types supported by the serializer. Fields that have unsupported types, as well as private fields or fields marked with the NonSerialized attribute, will be ignored.
Based on that description, and the example provided, it looks like it may only work with fields and not properties. I would try to make everything a member variable instead of a property and see if that fixes it.
You don't need to remove setters, just download and install Newtonsoft.Json for Unity3d
using Newtonsoft.Json;
Request req = JsonConvert.DeserializeObject<Request>(json);

Convert JSON object to Model in .NET

I have this JSON object returned from API:
[
{
"batchId": 789,
"debtId": 1841,
"dateAdded": "2021-07-27T16:01:39.41",
"debtCategoryId": 2,
"agreementNumber": 78262155,
"clientNumber": 1068055,
"clientName": "Client Two"
},
{
"batchId": 866,
"debtId": 1918,
"dateAdded": "2021-08-25T14:47:18.13",
"debtCategoryId": 2,
"agreementNumber": 1000140792,
"clientNumber": 11213287,
"clientName": "Client One"
}
]
I'm trying to convert that to a C# object which has this structure:
public class DebtConfirmationResponse
{
public List<DebtConfirmation> DebtConfirmations { get; set; }
}
Where DebtConfirmation has these properties:
public class DebtConfirmation
{
public int BatchId { get; set; }
public int DebtId { get; set; }
public string DateAdded { get; set; }
public int DebtCategoryId { get; set; }
public string AgreementNumber { get; set; } = string.Empty;
public string ClientNumber { get; set; } = string.Empty;
public string ClientName { get; set; } = string.Empty;
}
The error I'm getting is:
the json value could not be converted to the name of the model path $
linenumber 0 bytepositioninline 1
Is there anything wrong with the way how the model is being setup?
I also tried converting to the same model with batch id only as a property and I got the same message.
You define AgreementNumber, ClientNumber as strings in your C# code, but this properties is numbers in json, so you have to define it as longs.
And the another point is that you don't need a wrapper around DebtConfirmation class. Deserealize your json into ICollection, IList or just List of DebtConfirmation objects.
I used the quicktype.io for retrieving C# classes from json example you provide. This is very helpful for those who doesn't want to manually generate the models for their JSON strings.
Here is the code sample.
The output is:
789
866
using System.Text.Json;
using System.Text.Json.Serialization;
string json = "[\n {\n \"batchId\": 789,\n \"debtId\": 1841,\n \"dateAdded\": \"2021-07-27T16:01:39.41\",\n \"debtCategoryId\": 2,\n \"agreementNumber\": 78262155,\n \"clientNumber\": 1068055,\n \"clientName\": \"Client Two\"\n },\n {\n \"batchId\": 866,\n \"debtId\": 1918,\n \"dateAdded\": \"2021-08-25T14:47:18.13\",\n \"debtCategoryId\": 2,\n \"agreementNumber\": 1000140792,\n \"clientNumber\": 11213287,\n \"clientName\": \"Client One\"\n }\n]";
var data = JsonSerializer.Deserialize<ICollection<DebtConfirmation>>(json);
foreach (DebtConfirmation current in data)
{
Console.WriteLine(current.BatchId);
}
public partial class DebtConfirmation
{
[JsonPropertyName("batchId")]
public long BatchId { get; set; }
[JsonPropertyName("debtId")]
public long DebtId { get; set; }
[JsonPropertyName("dateAdded")]
public DateTimeOffset DateAdded { get; set; }
[JsonPropertyName("debtCategoryId")]
public long DebtCategoryId { get; set; }
[JsonPropertyName("agreementNumber")]
public long AgreementNumber { get; set; }
[JsonPropertyName("clientNumber")]
public long ClientNumber { get; set; }
[JsonPropertyName("clientName")]
public string ClientName { get; set; }
}
You can use #GeorgyTarasov's answer. But it is definitely not the simplest option.
You can simply use JsonNamingPolicy.CamelCase.
Then you would simply do...
var options = new JsonSerializerOptions
{
DictionaryKeyPolicy = JsonNamingPolicy.CamelCase,
};
var ret = JsonSerializer.Deserialize<DebtConfirmation>(payload, options);
If you are using AspNet Core, you can register the option here
services.AddControllers()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase;
});

Why is my JSON model not parsing not working for List of string only?

I have the following JSON data structure stored in a file, I have made a JSON data model in C# using the following tool JsonToCsharp. Usually, this tool is perfect and makes me awesome data models, but this time, for an unknown reason, each time I parse the JSON content, all string lists are null.
{
"Targets": [
{
"Name": "myTarget",
"Sharpmakes": [
{
"Name": "myTarget_v01",
"Dest": "/myTarget/folder/destination"
}
],
"Includes": [
"default_files" // <= This will always be null
]
},
{
"Name": "default_files",
"Directories": [
{
"Source": "/default/utils",
"Dest": "/utils",
"Includes": [ "*.bat", "*.ini", "*.txt", "*.xml", "*.json" ] // <= This will always be null
},
],
},
],
}
This is the code I am using for parsing my JSON:
try
{
var jsonContent = System.IO.File.ReadAllText(packageDefinitionJsonConfigPath);
return JsonConvert.DeserializeObject<Package>(jsonContent);
}
catch (Exception exception)
{
Log.Error($"Could not parse the json \n\n{packageDefinitionJsonConfigPath}");
throw exception;
}
Nothing is quite special about this code snippet, it's a simple NewtonSoft JSON parse.
And here are my generated models that JsonToCsharp gave me, (which looks just fine)...
[JsonObject]
public class Package
{
[JsonProperty("Targets")]
public List<Target> Targets { get; set; }
}
public class Sharpmake
{
[JsonProperty("Name")]
public string Name { get; set; }
[JsonProperty("Excludes")]
public IList<string> Excludes { get; set; }
[JsonProperty("Dest")]
public string Dest { get; set; }
[JsonProperty("Includes")]
public IList<string> Includes { get; set; }
}
public class File
{
[JsonProperty("Source")]
public string Source { get; set; }
[JsonProperty("Dest")]
public string Dest { get; set; }
}
public class Directory
{
[JsonProperty("Source")]
public string Source { get; set; }
[JsonProperty("Dest")]
public string Dest { get; set; }
[JsonProperty("Includes")]
public IList<string> Includes { get; set; }
}
public class Target
{
[JsonProperty("Name")]
public string Name { get; set; }
[JsonProperty("Sharpmakes")]
public IList<Sharpmake> Sharpmakes { get; set; }
[JsonProperty("Includes")]
public IList<string> Includes { get; set; }
[JsonProperty("Files")]
public IList<File> Files { get; set; }
[JsonProperty("Directories")]
public IList<Directory> Directories { get; set; }
}
public class RootObject
{
[JsonProperty("Targets")]
public IList<Target> Targets { get; set; }
}
Does anyone have an idea why my model could work just fine for everything except for my string lists? There nothing out of the extraordinary in this code snippet, so I'm really clueless here...
Updating my package to the latest version of Json.Net (12.0.2) seems to have fixed the issue
That would match with the release notes fixes
https://github.com/JamesNK/Newtonsoft.Json/releases/tag/12.0.2

Deserialize nested JSON Response with RestSharp Client

I'd like to consume a REST Api and deserialize the nested JSON Response. For that purpose I tried to create some POCO classes which represent the JSON Response [1].
The response looks like this:
{
"success": true,
"message": "OK",
"types":
[
{
"name": "A5EF3-ASR",
"title": "ITIL Foundation Plus Cloud Introduction",
"classroomDeliveryMethod": "Self-paced Virtual Class",
"descriptions": {
"EN": {
"description": "some Text null",
"overview": null,
"abstract": "Some other text",
"prerequisits": null,
"objective": null,
"topic": null
}
},
"lastModified": "2014-10-08T08:37:43Z",
"created": "2014-04-28T11:23:12Z"
},
{
"name": "A4DT3-ASR",
"title": "ITIL Foundation eLearning Course + Exam",
"classroomDeliveryMethod": "Self-paced Virtual Class",
"descriptions": {
"EN": {
"description": "some Text"
(...)
So I created the following POCO classes:
public class Course
{
public bool success { get; set; }
public string Message { get; set; }
public List<CourseTypeContainer> Type { get; set; }
}
/* each Course has n CourseTypes */
public class CourseType
{
public string Name { get; set; }
public string Title { get; set; }
public List<CourseTypeDescriptionContainer> Descriptions { get; set; }
public DateTime LastModified { get; set; }
public DateTime Created { get; set; }
}
public class CourseTypeContainer
{
public CourseType CourseType { get; set; }
}
/* each CourseType has n CourseTypeDescriptions */
public class CourseTypeDescription
{
public string Description { get; set; }
public string Overview { get; set; }
public string Abstract { get; set; }
public string Prerequisits { get; set; }
public string Objective { get; set; }
public string Topic { get; set; }
}
public class CourseTypeDescriptionContainer
{
public CourseTypeDescription CourseTypeDescription { get; set; }
}
And this is the API Code:
var client = new RestClient("https://www.someurl.com");
client.Authenticator = new HttpBasicAuthenticator("user", "password");
var request = new RestRequest();
request.Resource = "api/v1.0/types";
request.Method = Method.GET;
request.RequestFormat = DataFormat.Json;
var response = client.Execute<Course>(request);
EDIT 1: I found a Typo, the Type property in AvnetCourse should be named Types:
public List<AvnetCourseTypeContainer> Type { get; set; } // wrong
public List<AvnetCourseTypeContainer> Types { get; set; } // correct
Now the return values look like:
response.Data.success = true // CORRECT
repsonse.Data.Message = "OK" // CORRECT
response.Data.Types = (Count: 1234); // CORRECT
response.Data.Types[0].AvnetCourseType = null; // NOT CORRECT
EDIT 2: I implemented the Course.Types Property using a List<CourseType> instead of a List<CourseTypeContainer>, as proposed by Jaanus. The same goes for the CourseTypeDescriptionContainer:
public List<CourseTypeContainer> Type { get; set; } // OLD
public List<CourseTypeDescriptionContainer> Descriptions { get; set; } // OLD
public List<CourseType> Type { get; set; } // NEW
public List<CourseTypeDescription> Descriptions { get; set; } // NEW
Now the response.Data.Types finally are properly filled. However, the response.Data.Types.Descriptions are still not properly filled, since there is an additional language layer (e.g. "EN"). How can I solve this, without creating a PACO for each language?
EDIT 3: I had to add an additional CourseTypeDescriptionDetails class, where I would store the descriptive Data. In my CourseTypeDescription I added a property of the Type List for each language. Code Snippet:
public class AvnetCourseType
{
public List<CourseTypeDescription> Descriptions { get; set; }
// other properties
}
public class CourseTypeDescription
{
public List<CourseTypeDescriptionDetails> EN { get; set; } // English
public List<CourseTypeDescriptionDetails> NL { get; set; } // Dutch
}
public class CourseTypeDescriptionDetails
{
public string Description { get; set; }
public string Overview { get; set; }
public string Abstract { get; set; }
public string Prerequisits { get; set; }
public string Objective { get; set; }
public string Topic { get; set; }
}
It works now, but I need to add another property to CourseTypeDescription for each language.
OLD: The return values are
response.Data.success = true // CORRECT
repsonse.Data.Message = "OK" // CORRECT
response.Data.Type = null; // WHY?
So why does my response.Type equal null? What am I doing wrong?
Thank you
Resources:
[1] RestSharp Deserialization with JSON Array
Try using this as POCO:
public class Course
{
public bool success { get; set; }
public string message { get; set; }
public List<CourseTypeContainer> Types { get; set; }
}
Now you have list of CourseTypeContainer.
And CourseTypeContainer is
public class CourseTypeContainer
{
public CourseType CourseType { get; set; }
}
So when you are trying to get response.Data.Types[0].AvnetCourseType , then you need to have field AvnetCourseType inside CourseTypeContainer
Or I think what you want is actually this public List<CourseType> Types { get; set; }, you don't need a container there.
Just in case this helps someone else, I tried everything here and it still didn't work on the current version of RestSharp (106.6.2). RestSharp was completely ignoring the RootElement property as far as I could tell, even though it was at the top level. My workaround was to manually tell it to pull the nested JSON and then convert that. I used JSON.Net to accomplish this.
var response = restClient.Execute<T>(restRequest);
response.Content = JObject.Parse(response.Content)[restRequest.RootElement].ToString();
return new JsonDeserializer().Deserialize<T>(response);
I used http://json2csharp.com/ to create C# classes from JSON.
Then, renamed RootObject to the ClassName of the model file I'm creating
All the data in the nested json was accessible after RestSharp Deserializitaion similar to responseBody.data.Subject.Alias
where data, Subject and Alias are nested nodes inside the response JSON received.

Categories