Read a JSON property with NULL handling using JSON.Net - c#

I have a JSON structure like below to show the details of a specific candidate
It can be either null or can contain some details like below
"details": {
"gender": {
"id": 3,
"props": {
"name": "male"
}
}
}
or as null
"details": {
"gender": null
}
To read the value of gender i tried
string _gender = (string)result["details"]["gender"]["props"]["name"];
This will works in non null cases . But if its null then this code returns an exception
So to check first is it null or not and if not null try to read the value, i tried below code
string _gender = (string)result["details"]["gender"];
if (!string.IsNullOrEmpty(_gender))
{
_gender = (string)result["details"]["gender"]["props"]["name"];
}
But i am getting the exception that not possible to convert object to string. So how to read a JSON property with proper null handling \

I strongly suggest you to deserialize the json as known type.
public class Props
{
public string name { get; set; }
}
public class Gender
{
public int id { get; set; }
public Props props { get; set; }
}
public class Details
{
public Gender gender { get; set; }
}
public class JsonObject
{
public Details details { get; set; }
}
Then perform deserialization;
var jsonObject = JsonConvert.DeserializeObject<List<JsonObject>>(json);
foreach (var o in jsonObject)
{
var genderName = o?.details?.gender?.props?.name;
}
In this way, you can handle the possible null values and get strongly typed object.
EDIT
Also, in your code, you are trying to convert an object to string and it is completely wrong. It seems that gender object is a complex type. So you can't convert it to string and you should modify your code like this;
object _gender = result["details"]["gender"];
if (_gender != null)
{
string genderName = result["details"]["gender"]["props"]["name"].ToString();
}

Keep in mind that jToken[xx..][yy..] is not a string, it is a JToken object, therefore we cannot explicitly cast it to string. If we want to convert it to string we'd have to call ToString() on it explicitly (which in terms calls JToken's overridden implementation of .ToString).
First we need to check that Jtoken has values for that we have method .HasValues.
Later when we know for certain that there is a string in ["name"]
property we can use either - explicit cast or .ToString() again
string _gender;
var genderToken = jToken["details"]["gender"];
if (genderToken.HasValues)
{
_gender = genderToken["props"]["name"].ToString();
}

Related

deserialize a object to return empty string for object type nullable datetime

I am trying to deserialize a object. It returns null for a object of nullable datetime (Collected)
public async var GetOrders()
{
var orders = await db
.GetDataTableAsync($"query")
.GetRows()
.Select(r => new
{
OrderContent = (dynamic) JsonConvert.DeserializeObject(r["json"].ToString())
}).ToList();
return orders;
}
public class OrderInfo
{
public DateTime? Collected { get; set; }
public string TypeID { get; set; }
}
result:
{
"Collected": null,
"TypeID": 1
}
expected:
{
"Collected": "",
"TypeID": 1
}
Try to use custom json serializer. Pls. see two articles:
Json Convert empty string instead of null
enter link description here

C# Json serialize different types

I'm trying to retrieve all data from a JSON file to my C# application.
But now the problem is that the field "info" in my json file is sometimes from the type string but it can also be the type object.
{
[
{
"id":"147786",
"canUpdate":true,
"canDelete":true,
"canArchive":true,
"hasChildren":false,
"info": "Test"
},
{
"id":"147786",
"canUpdate":true,
"canDelete":true,
"canArchive":true,
"hasChildren":false,
"info": [{"id"="1","messages":"true"}]
}
]
}
well my model you can see here below, when there are only strings in my json file i can retrieve the data without any exception but when there are also objects in the info field then i get the error can't convert the value.
Is there a way to fix this on an easy way?
public string id { get; set; }
public string canUpdate { get; set; }
public string info { get; set; }
As an option you can define the info as dynamic:
public dynamic info { get; set; }
Example
Consider the following json string:
string json = #"
[
{ 'P1': 'X', 'P2': 'Y' },
{ 'P1': 'X', 'P2': [
{'P11':'XX', 'P22':'YY'},
{'P11':'XX', 'P22':'YY'}]
}
]";
You can define such model to deserialize it:
public class C
{
public string P1 { get; set; }
public dynamic P2 { get; set; }
}
And deserialize it like this:
var obj = JsonConvert.DeserializeObject<C[]>(json);
Note
If the number of dynamic properties is too much then usually there is no point in creating the class and the following code will be enough:
var obj = (dynamic)JsonConvert.DeserializeObject(json);

Convert complex JSON to Generic List using Newtonsoft

Below is a Json :
[{
"Result": {
"description": "Application Security Supp Specialist",
"code": "40000003"
}
}, {
"Result": {
"description": "Gvt Cyber Intelligence Specialist",
"code": "40001416"
}
}, {
"Result": {
"description": "Gvt Record Retention Specialist",
"code": "40001428"
}
}]
And below is the class structure which i have created as i need to fill this into a C# object.
I am trying to create a collection of RulesEngineOutput and fill it with the json contents.
public class RulesEngineOutput
{
[JsonProperty("description")]
public string Description { get; set; }
[JsonProperty("code")]
public string Code { get; set; }
}
public class RulesEngineOutputCollection
{
public IEnumerable<RulesEngineOutput> ProbableRoles { get; set; }
}
I am trying to achieve this using below code :
var bodyJson = JsonConvert.SerializeObject(bodyString);
RulesEngineOutputCollection result = new RulesEngineOutputCollection();
foreach (var item in bodyJson)
{
result = JsonConvert.DeserializeObject<RulesEngineOutputCollection>(item.ToString());
}
But this is throwing exception as the item gets a char, what i am thinkiong is that i need to pass a JSON object in the loop but i am not able to get one.
Everytime i get is a JSON string.
Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'RulesEngineOutputCollection' because the type requires a JSON object (e.g. {\"name\":\"value\"}) to deserialize correctly.\r\nTo fix this error either change the JSON to a JSON object (e.g. {\"name\":\"value\"}) or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List that can be deserialized from a JSON array.
The problem is that you have an intermediary object between your RulesEngineOutput and your collection. You need to restructure your objects as such:
public class RulesEngineOutput
{
[JsonProperty("description")]
public string Description { get; set; }
[JsonProperty("code")]
public string Code { get; set; }
}
public class RulesEngineOutputResult
{
public RulesEngineOutput Result { get; set; }
}
public class RulesEngineOutputCollection
{
public IEnumerable<RulesEngineOutputResult> ProbableRoles { get; set; }
}
And then when you have this restructuring done, you can deserialize directly to your RulesEngineOutputCollection instead of to an object and iterating and deserializing again.
result = JsonConvert.DeserializeObject<RulesEngineOutputCollection>(bodyString);
Thanks a lot Max,Nathan and others. So finally i made some changes in code and below is the code which i changed tomake the things work :
var jToken = JObject.Parse(responseContent);
var bodyString = jToken.SelectToken("body");
var bodyJson = JsonConvert.SerializeObject(bodyString);
List<RulesEngineOutput> result = new List<RulesEngineOutput>();
try
{
foreach (var item in bodyString)
{
var formattedItem = item.SelectToken("Result");
var resultItem = JsonConvert.DeserializeObject<RulesEngineOutput>(formattedItem.ToString());
result.Add(resultItem);
}
}
Hope it helps others as well.
As Nathan Werry said, you have an object layered into another object and because of that, you cannot deserialize the data in the way you want it. However, you can work around that if you first create an array of these results and assign it later to your ProbableRoles property:
var rules = new RulesEngineOutputCollection
{
ProbableRoles = JsonConvert.DeserializeObject<Result[]>(bodyString).Select(r => r.Data).ToList()
};
public class Result
{
[JsonProperty("Result")]
public RulesEngineOutput Data { get; set; }
}
Everything else stays the same. You basically create a new list out of your array of results. I could also assign the Select() result directly (without calling .ToList()) but this ensures that the object actually has the data and not just a reference to an enumeration.

Not able to use JIL Serializer ExcludeNulls Option

I am not able to use JIL's Exclude Null option. Instead, I get an exception:
JIL.DeserializationException: 'Expected digit'
Below are code snippets.
public Task<InputFormatterResult> ReadAsync(InputFormatterContext context)
{
if (context == null) throw new ArgumentNullException(nameof(context));
var request = context.HttpContext.Request; if (request.ContentLength == 0)
{
if (context.ModelType.GetTypeInfo().IsValueType)
return InputFormatterResult.SuccessAsync(Activator.CreateInstance(context.ModelType));
else return InputFormatterResult.SuccessAsync(null);
}
var encoding = Encoding.UTF8;//do we need to get this from the request im not sure yet
using (var reader = new StreamReader(context.HttpContext.Request.Body))
{
var model = Jil.JSON.Deserialize(reader, context.ModelType, Jil.Options.ExcludeNulls);
return InputFormatterResult.SuccessAsync(model);
}
}
1) Model type
public class PaymentTypeBORequest
{
public int pkId { get; set; }
public string description { get; set; }
public bool isSystem { get; set; }
public bool isActive { get; set; }
}
2) JSON String:
{
"pkId":null,
"description": "Adjustment",
"isSystem": true,
"isActive": true
}
The description for the excludeNulls option is:
whether or not to write object members whose value is null
(emphasis mine)
This suggests that it only affects serialisation operations and not deserialisation operations.
When serialising an object with excludeNulls set to true, Jil will not write properties to the JSON if they have null values. In your example, you're deserialising into a PaymentTypeBORequest object, which itself does not support null values for the pkId property, as it's not nullable.
In order to resolve your specific issue, you can simply set pkId to be a nullable int, like so:
public class PaymentTypeBORequest
{
public int? pkId { get; set; }
...
}
If you want to also allow null for the non-nullable isSystem and isActive properties, you can perform the same operations on those fields.

ServiceStack.Text json deserialization creates wrong object instead of throwing on invalid json input string

When I try to deserialise this invalid json string ( }] missing in the end) :
[{"ExtId":"2","Name":"VIP sj�lland","Mobiles":["4533333333","4544444444"]
By doing this:
var result = JsonSerializer.DeserializeFromString<T>(str);
The ServiceStack json deserializer accepts the string, but it creates a wrong object, because I end up with a C# object having these values:
ExtId : "2" // ok fine.
Name: "VIP sj�lland" // ok fine
Mobiles: ["4533333333","4544444444", "544444444"]// Aarg! An array with 3 objects ?!?
// There were only two in the JSON string.
In this case it would be much better to have an exception thrown instead of continuing with bad data. Therefore I tried using:
JsConfig.ThrowOnDeserializationError = true;
just before calling DeserializeFromString but no exception was thrown. In January I asked this question Configure ServiceStack.Text to throw on invalid JSON and the answer was that ServiceStack is favoring resilence and that I could make a pull request in GitHub.
Is this still the case? And have anyone done it already, saving me the trouble? Otherwise, I am on a very tight schedule, so if anyone has some code or suggestions for how to create an option-flag for making ServiceStack throw on deserialization errors, please reply here, so that I can get this done faster.
This is resolved in ServiceStack.Text v4+ which by default doesn't populate incomplete collections, e.g:
public class Poco
{
public string ExtId { get; set; }
public string Name { get; set; }
public string[] Mobiles { get; set; }
}
var json = "[{\"ExtId\":\"2\",\"Name\":\"VIP sj�lland\",\"Mobiles\":[\"4533333333\",\"4544444444\"]";
var dto = json.FromJson<Poco[]>();
Assert.That(dto[0].ExtId, Is.EqualTo("2"));
Assert.That(dto[0].Name, Is.EqualTo("VIP sj�lland"));
Assert.That(dto[0].Mobiles, Is.Null);
Or if preferred can throw on Error:
JsConfig.ThrowOnDeserializationError = true;
Assert.Throws<SerializationException>(() =>
json.FromJson<Poco[]>());
C# is a little bit picky when it comes to JSON. Following would be valid! Note i do not have anonymous object array as the default element.
{
"ExtItem": [
{
"ExtId": "2",
"Name": "VIPsj�lland",
"Mobiles": [
"4533333333",
"4544444444"
]
}
]
}
If i generate a POCO from this I get
public class Rootobject
{
public Extitem[] ExtItem { get; set; }
}
public class Extitem
{
public string ExtId { get; set; }
public string Name { get; set; }
public string[] Mobiles { get; set; }
}
I personally use extension method to string
public static class Extensions
{
public static bool DeserializeJson<T>(this String str, out T item)
{
item = default(T);
try
{
item = new JavaScriptSerializer().Deserialize<T>(str);
return true;
}
catch (Exception ex)
{
return false;
}
}
}
This would enable me to write:
Rootobject ext;
const string validJson = #"
{
""ExtItem"": [
{
""ExtId"":""2"",
""Name"":""VIPsj�lland"",
""Mobiles"":[
""4533333333"",
""4544444444""
]
}
]
}";
if (validJson.DeserializeJson(out ext))
{ //valid input
// following would print 2 elements : 4533333333, 4544444444
Console.WriteLine(string.Join(", ", ext.ExtItem.First().Mobiles));
} //invalid input
I tried this an have the exception thrown when I missed the } at the end.
In C#, the format for JSON is {"name", "value"} not [{"name", "value"}].
class M
{
public string ExtId { get; set; }
public string Name { get; set; }
public List<string> Mobiles { get; set; }
}
string str = "{\"ExtId\":\"2\",\"Name\":\"VIP\",\"Mobiles\":[\"4533333333\",\"4544444444\"]";
M m = JsonConvert.DeserializeObject<M>(str);
When run this, you will get error as a } is missing.
Using:
string str = "{\"ExtId\":\"2\",\"Name\":\"VIP\",\"Mobiles\":[\"4533333333\",\"4544444444\"]}";
The object is deserialized fine.
You can see the JSON of this object from:
string s = JsonConvert.SerializeObject(m);
I just use the Newtonsoft.Json T JsonConvert.DeserializeObject<T>(string value) and it throws an exception;
If I use the object JsonConvert.DeserializeObject(string value) this one it creates the correct object by simply placing the missing }]
I found that this is a very reliable and fast library.

Categories

Resources