Is it possible to have JObject.Parse ignore missing fields?
From my example below you can see that I have declared a class Address and using JsonProperty to specifying alternate field names.
I have provided 3 examples, there are 3 JSON strings which have a slightly different structure, only Example 1 matches and returns an object, Examples 2 and 3 return a null because there is a missing field.
Is there a way to use other JsonProperty's to allow them to be ignored if not provided?
public class Address
{
[JsonProperty("flat_number")]
public string FlatNumber { get; set; }
[JsonProperty("house_number")]
public string HouseNumber { get; set; }
[JsonProperty("address")]
public string Address1 { get; set; }
[JsonProperty("address2")]
public string Address2 { get; set; }
[JsonProperty("town")]
public string Town { get; set; }
[JsonProperty("postcode")]
public string Postcode { get; set; }
}
private static T TryParse<T>(string json) where T : new()
{
var jSchemaGenerator = new JSchemaGenerator();
const string license = "license";
License.RegisterLicense(license);
var jSchema = jSchemaGenerator.Generate(typeof(T));
var jObject = JObject.Parse(json);
return jObject.IsValid(jSchema) ? JsonConvert.DeserializeObject<T>(json) : default(T);
}
//Example 1 with house_number and flat_number
const string json = "{\"house_number\":\"40\",\"flat_number\":\"82\",\"address\":\"Somewhere\",\"address2\":\"Over\",\"town\":\"There\",\"postcode\":\"ZZ991AA\"}";
//Example 2 with house_number but not flat_number
//const string json = "{\"house_number\":\"40\",\"address\":\"Somewhere\",\"address2\":\"Over\",\"town\":\"There\",\"postcode\":\"ZZ991AA\"}";
//Example 3 with flat_number but not house_number
//const string json = "{\"flat_number\":\"82\",\"address\":\"Somewhere\",\"address2\":\"Over\",\"town\":\"There\",\"postcode\":\"ZZ991AA\"}";
var tryParse = TryParse<AddressTest>(json);
if (tryParse != null)
{
}
You can use JsonSerializerSettings to perform this operation. This will ignore your
missing members.
var jsonSerializerSettings = new JsonSerializerSettings();
jsonSerializerSettings.MissingMemberHandling = MissingMemberHandling.Ignore;
JsonConvert.DeserializeObject<YourClass>(jsonResponse, jsonSerializerSettings);
If you json contains some key/value pair as dynamic means you don't know if these key/value pairs is exist in json or not
If your objects are not fixed and data must be configurable then Newtonsoft.json has one feature that to be use here and that is [JsonExtensionData]. Read more
Extension data is now written when an object is serialized. Reading and writing extension data makes it possible to automatically round-trip all JSON without adding every property to the .NET type you’re deserializing to. Only declare the properties you’re interested in and let extension data do the rest.
Now your house_number and flat_number will be collected in [JsonExtensionData] so you no need to handle missing fields anymore.
So your Address class will be
public class Address
{
[JsonExtensionData]
public IDictionary<string, JsonToken> extensionData { get; set; }
[JsonProperty("address")]
public string Address1 { get; set; }
[JsonProperty("address2")]
public string Address2 { get; set; }
[JsonProperty("town")]
public string Town { get; set; }
[JsonProperty("postcode")]
public string Postcode { get; set; }
}
After a little more digging I found a JsonProperty that allows nulls to be ignored, by applying NullValueHandling = NullValueHandling.Ignore to both FlatNumber and HouseNumber all examples return an object. Therefore modifying the Address class as per my example below works in conjunction with my original code.
public class Address
{
[JsonProperty("flat_number", NullValueHandling = NullValueHandling.Ignore)]
public string FlatNumber { get; set; }
[JsonProperty("house_number", NullValueHandling = NullValueHandling.Ignore)]
public string HouseNumber { get; set; }
[JsonProperty("address")]
public string Address1 { get; set; }
[JsonProperty("address2")]
public string Address2 { get; set; }
[JsonProperty("town")]
public string Town { get; set; }
[JsonProperty("postcode")]
public string Postcode { get; set; }
}
Related
I am receiving a json file via api, that json file will be converted into a class that I cant touch and has around 400 properties. the json is using for the key names CamelCase and in the same json some keys are in the format of snake_case.
I am currently using System.Text.Json but open to change to Newtonsoft.json is needed.
I tried to create a JsonSnakeCaseNamingPolicy class (only converting the property names to snake_case) and used in the JsonSerializerOptions like this:
var deserializeOptions = new JsonSerializerOptions()
{
PropertyNameCaseInsensitive = true,
PropertyNamingPolicy = new JsonSnakeCaseNamingPolicy()
};
var flexImport = JsonSerializer.Deserialize<List<FlexImport>>(input.MappedObjectJson, deserializeOptions);
But then the properties in CamelCase don't get populated. Any idea on how to achieve this situation?
This the json sample:
[{\"BatchId\":123,\"Title_Id\":123,\"CurrentNumber\":\"aa705128\",\"address\":\"122 BLACKSGATE EN\",\"curr_interest_rate\":4},{\"BatchId\":2,\"Title_Id\":1,\"CurrentNumber\":\"27705128\",\"address\":\"90 ARMA DR\",\"curr_interest_rate\":5},{\"BatchId\":2,\"Title_Id\":2,\"CurrentNumber\":\"30877674\",\"address\":\"6485 N SIN CIR\",\"curr_interest_rate\":4}]"
And here is part of the destination class:
public class FlexImport
{
public long BatchId { get; set; }
public long TitleId { get; set; }
public string CurrentNumber { get; set; }
public string Address { get; set; }
public decimal? CurrInterestRate { get; set; }
}
Use JSON attributes
public class FlexImport
{
public long BatchId { get; set; }
[JsonPropertyName("Title_Id")]
public long TitleId { get; set; }
public string CurrentNumber { get; set; }
[JsonPropertyName("address")]
public string Address { get; set; }
[JsonPropertyName("curr_interest_rate")]
public decimal? CurrInterestRate { get; set; }
}
Etc. Adjust as needed.
This stumps me. Here are my simplified C# class definitions:
public class Countries
{
string TotalCount { get; set; }
public List<Ctry> Country { get; set; }
}
public class Ctry
{
string CountryId { get; set; }
string CountryName { get; set; }
}
The REST call I make succeeds and returns the following JSON which I can see in 'content' variable:
{"TotalCount":1,"Country":[{"CountryId":1,"CountryName":"USA"}]}
Here is my c# deserializing code:
var content = response.Content;
countryList = JsonConvert.DeserializeObject<Countries>(content);
After deserialization, I expect the country data to be in countryList object. However no data shows up in countryList! What gives? No exceptions or errors either!
Your issue is that JSON.NET defaults to camel-case property names. This is what your code is looking for by default:
{"country":[{"countryId":"1","countryName":"USA"}]}
You need to manually declare the JSON.NET property names for your model:
public class Countries
{
[JsonProperty(PropertyName = "TotalCount")]
string TotalCount { get; set; }
[JsonProperty(PropertyName = "Country")]
public List<Ctry> Country { get; set; }
}
public class Ctry
{
[JsonProperty(PropertyName = "CountryId")]
string CountryId { get; set; }
[JsonProperty(PropertyName = "CountryName")]
string CountryName { get; set; }
}
I tested this and it works with your data.
As a side note, I declared all of the property names because I like to maintain manual control over the serialization and deserialization, in your case, you could squeak by with just declaring the multi-case words.
You can also fix this by adjusting the protection level for your properties, if you don't want to manually define property names:
public class Countries
{
public string TotalCount { get; set; }
public List<Ctry> Country { get; set; }
}
public class Ctry
{
public string CountryId { get; set; }
public string CountryName { get; set; }
}
This way JSON.NET can match the property names automatically if they are publicly accessible.
#Tom W - JSON.NET will automatically convert types when possible, int to string and string to int are fine.
Currently I am building some functionality that takes an object (in my case a blog post) in JSON format and validates it meets the 'Post' schema before deserializing it into an object. This is my current solution -
{
const string rawPost = #"{
""userId"": 1,
""id"": 200,
""title"": ""hello"",
""body"": ""body""
}";
JSchemaGenerator generator = new JSchemaGenerator();
JSchema schema = generator.Generate(typeof(Post));
var isPost = IsValid(JObject.Parse(rawPost), schema);
if (isPost)
{
var post = JsonConvert.DeserializeObject<Post>(rawPost);
}
}
public static bool IsValid(JObject jObject, JSchema jSchema)
{
return jObject.IsValid(jSchema);
}
public class Post
{
public int userId { get; set; }
public int id { get; set; }
public string title { get; set; }
public string body { get; set; }
}
}
Currently I do not like the fact that my 'Post' object in this case has incorrectly cased properties and breaks normal conventions - Which means the object is not very reusable. Is there another way of achieving the same thing with Pascal Casing?
You could decorate the properties of your class with the NewtonSoft JsonProperty attribute, setting the PropertyName as follows:
using Newtonsoft.Json;
public class Post
{
[JsonProperty(PropertyName = "userId")]
public int UserId { get; set; }
[JsonProperty(PropertyName = "id ")]
public int Id { get; set; }
[JsonProperty(PropertyName = "title ")]
public string Title { get; set; }
[JsonProperty(PropertyName = "body ")]
public string Body { get; set; }
}
The JsonSerializer object will use these in its deserialization.
Edit: or as per the linked duplicate question
I want to deserialize a Json string from a php website. Unfortunately every time I try it, it will return null for medianPrice....Why?
public class PriceInfo
{
public string success { get; set; }
public double lowestPrice { get; set; }
public string volume { get; set; }
public string medianPrice { get; set; }
}
WebClient client = new WebClient();
string url = "http://steamcommunity.com/market/priceoverview/?country=US¤cy=1&appid=730&market_hash_name=" + name;
byte[] html = client.DownloadData(url);
UTF8Encoding utf = new UTF8Encoding();
string return_value = utf.GetString(html);
PriceInfo priceInfo = new JavaScriptSerializer().Deserialize<PriceInfo>(return_value);
if( Double.Parse(priceInfo.medianPrice) > 0.15 )
{
string blablu = "hello";
}
The Json which returns from the website is the following:
{"success":true,"lowest_price":"$0.04","volume":"3,952","median_price":"$0.02"}
I hope you can help me!
I am strongly recommending that you try using Newtonsoft.Json
you will find that it is easier to handle your Jason objects
your code will be like this (Untested)
PriceInfo defaultCallResult = JsonConvert.DeserializeObject<PriceInfo>(return_value);
Your JSON property is named "median_price" (with an underscore), but your C# property is "medianPrice".
You could use Json.NET which will allow you to change the mapping using attributes.
Using Json.NET, decorate your medianPrice property as follows:
[JsonProperty(PropertyName = "median_price")]
public string medianPrice { get; set; }
I'm not sure how JavaScriptSerializer succeeds in parsing your class at all, as the keys hardly match match the class properties.
JavaScriperSerializer is obsolete, I'd recommend you use another serializer, such as Json.NET:
public class PriceInfo
{
[JsonProperty("success")]
public string Success { get; set; }
[JsonProperty("lowest_price")]
public double LowestPrice { get; set; }
[JsonProperty("volume")]
public string Volume { get; set; }
[JsonProperty("median_price")]
public string MedianPrice { get; set; }
}
And when you want to deserialize:
PriceInfo priceInfo = JsonConvert.DeserializeObject<PriceInfo>(returnValue);
First of all, as mentioned in the other answers, your property names do not match.
So take
public class PriceInfo
{
public string success { get; set; }
public string lowest_price { get; set; }
public string volume { get; set; }
public string median_price { get; set; }
}
edit: as mentioned by Yuval, you cannot use JsonProperty with JavaScriptSerializer so you need to stick with the property names from the json.
Then, there is currency information in the json. So you need to get rid of these:
string return_value = "{\"success\":true,\"lowest_price\":\"$0.04\",\"volume\":\"3,952\",\"median_price\":\"$0.02\"}";
string return_valueconverted = HttpUtility.HtmlDecode(return_value);
PriceInfo priceInfo = new JavaScriptSerializer().Deserialize<PriceInfo>(return_valueconverted);
priceInfo.lowest_price = priceInfo.lowest_price.TrimStart('$');
priceInfo.median_price = priceInfo.median_price.TrimStart('$');
As you can see, this does HtmlDecode these values and afterwards trims the dollar sign from the value.
See more about Html character set here:
http://www.w3.org/MarkUp/html-spec/html-spec_13.html
I have a one Model it's look like
public class DataClass
{
public string Name { get; set; }
public string Address { get; set; }
public string ContactNo { get; set; }
}
and I tried to convert in Json request using below mentioned code.
var l=new List<Data>();
l.Add(new Data(){Name="foo",Address ="bar",ContactNo =123});
l.Add(new Data(){Name="biz",Address ="baz"});
string json=JsonConvert.SerializeObject(l);
it will give me string like
[{"Name":"foo","Address":"bar","ContactNo":"123"},{"Name":"biz","Address":"baz","ContactNo":""}]
in output second ContactNo has a empty string but I don't need the field which has a no value or NULL .
can anyone please tell me what is the best way to avoid NULL or Empty field from Json request?
Thanks in Advance.
Change your model as below
public class Data
{
public string Name { get; set; }
public string Address { get; set; }
public int? ContactNo { get; set; }
}
and then serialize your object as below
var result = JsonConvert.SerializeObject(
l,
new JsonSerializerSettings()
{
NullValueHandling = NullValueHandling.Ignore
});
I assume you are using Json.Net.
You can use the System.ComponentModel.DefaultValueAttribute. This allows you to mark a property to use a different default value than null.
So, if you want empty strings to be ignored in your JSON output you can update the model class to look like this:
public class DataClass
{
[DefaultValue("")]
public string Name { get; set; }
[DefaultValue("")]
public string Address { get; set; }
[DefaultValue("")]
public string ContactNo { get; set; }
}
Note that the SerializerSettings.DefaultValueHandling must be set to Ignore or IgnoreAndPopulate for this to be picked up.
A more thorough example of various approaches for reducing serialized json size is here:
http://james.newtonking.com/archive/2009/10/23/efficient-json-with-json-net-reducing-serialized-json-size
1.You may add a flag in the model class.
public class DataClass{
public bool isIllegal{get;set;}
public string Name { get; set; }
public string Address { get; set; }
public string ContactNo { get; set{isIllegal=!string.isNullOrEmpty(value);)}
}
2.You can filter data whose isIllegal is false after JsonConvert.SerializeObject(l).