IgnoreNullValue not working when serializing an Anonymous Object - c#

The Problem
I am creating an anonymous object in a method and assigning it properties from a class. Some of the properties can sometimes be empty (strings) and I eventually need to serialize the anonymous object to a JSON string.
When it gets serialized as JSON, I want to get rid of nulls or empty strings. I actually want to do this on a property by property basis (not generically ignoring all nulls) but it doesn't seem to be working at all!
The Code
public class Refrigerator {
public string Brand { get; set; }
public bool HasFood { get; set; }
public void Serialize() {
//ANONYMOUS OBJECT HERE
var fridge = new {
Brand = this.Brand; //THIS VALUE IS SOMETIMES AN EMPTY STRING!!
HasFood = this.HasFood;
}
var value = Newtonsoft.Json.JsonConvert.SerializeObject(fridge);
//value contains Brand as an empty string
}
}
What I've Tried
var fridge = new {
Brand = this.Brand;
HasFood = this.HasFood;
//JsonSerializer should check this function I thought?
ShouldSerializeBrand = new Func<bool>() {
if(String.IsNullOrEmpty(this.Brand){
return false;
}
return true;
}
}
//This should also work, but doesn't.......
var value = Newtonsoft.Json.JsonConvert.SerializeObject(fridge, new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Ignore
});
//JSON STILL contains Brand as an empty string!!! Its still there!
Any ideas why its not working?

One of the way for this could be that, you can use ExpandoObject for this like :
dynamic fridge = new ExpandoObject();
if(!String.IsNullOrEmpty(this.Brand))
{
fridge.Brand = this.Brand;
}
fridge.HasFood = this.HasFood;
This way the anonymous object will not have Brand property created unless it has a value. Now if the Brand is null of empty string, the property won't be created on the anonymous object we have created.
See this DEMO FIDDLE as example.

An empty string i.e. the literal "" is neither a null value nor the default for a string type. As such, both options from your JsonSerializerSettings will fail to do what you want. You can solve the problem by doing what you already tried, or by creating a custom JSONConverter to add in your specific serialization logic.
Sample code based off the Newtonsoft samples:
public class FridgeJsonConverter : JsonConverter
{
private readonly Type[] _types;
public FridgeJsonConverter(params Type[] types)
{
_types = types;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JToken t = JToken.FromObject(value);
if (t.Type != JTokenType.Object)
{
t.WriteTo(writer);
}
else
{
JObject o = (JObject)t;
var brandProp = o.Properties().First(x => x.Name == "Brand");
if(brandProp.Value.ToString() == string.Empty)
o.Remove(brandProp.Name);
o.WriteTo(writer);
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
}
public override bool CanRead
{
get { return false; }
}
public override bool CanConvert(Type objectType)
{
return _types.Any(t => t == objectType);
}
}
Then, in your calling code, do this:
string json = JsonConvert.SerializeObject(fridge, Formatting.Indented, new FridgeJsonConverter(typeof(Fridge)));

Related

C# System.Text.Json or Newtonsoft JSON properties marking for serialization control by attributes

I am just wonder if I can mark certain property of class instance via any attribute and during serialization serialize just those marked properties (and of-course by deserializing affect also only marked properties via attribute vice-versa in instance of the class - the rest of properties should remain same...).
I know how to identify those properties by reflection, but I do not want to make another Json serialization by myself.
[MyFirstAttribute]
public string A { get; set; } = "hi";
[MyFirstAttribute]
public int B { get; set; } = 13;
[MySecondAttribute]
public string C { get; set; } = "something";
as it documented here in this link you can create a custom CustomJsonConverter by inheriting from JsonConverter class.
And then use it like:
Employee employee = new Employee
{
FirstName = "James",
LastName = "Newton-King",
Roles = new List<string>
{
"Admin"
}
};
string json = JsonConvert.SerializeObject(employee, Formatting.Indented, new KeysJsonConverter(typeof(Employee)));
Console.WriteLine(json);
Based on #ArgeKumandan advice:
public class KeysJsonConverter : JsonConverter
{
private readonly Type[] _types;
public KeysJsonConverter(params Type[] types) => _types = types;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JToken t = JToken.FromObject(value);
if (t.Type != JTokenType.Object) t.WriteTo(writer);
else
{
JObject jo = new JObject();
foreach (PropertyInfo prop in value.GetType().GetProperties())
{
if (!prop.CanRead) continue;
object propVal = prop.GetValue(value, null);
if (propVal is null || !Attribute.IsDefined(prop, _types[0])) continue;
jo.Add(prop.Name, JToken.FromObject(propVal, serializer));
}
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
}
public override bool CanRead { get => false; }
public override bool CanConvert(Type objectType) => _types.Any(t => t == _types[0]);
}
and then usage:
// serialization
var json = JsonConvert.SerializeObject(objectInstance1, Formatting.Indented, new KeysJsonConverter(typeof(MyFirstAttribute)));
// deserialization to an existing instance that updates just the properties contained in JSON
JsonConvert.PopulateObject(jsonCommon, objectInstance2);

Concat names of nested values of a json file in c# ex: ` "obj_nested1_nested2" : "text"

I'm looking for a way to transform this c# object:
class BaseClass
{
public string Value1 {get; set;}
public NestedObject nestedObject {get;set;}
}
class NestedObject
{
public string NestedValue1 {get; set;}
}
Into this json:
{
"Value1": "value1",
"NestedObject_NestedValue1": "nestedValue1"
}
By concatening the names of the nested parameters to their parent's name
Using normal serialization, this code: var json= JsonConvert.SerializeObject(baseClass);
Would instead return a json like this one:
{
"Value1": "value1",
"NestedObject": {
"NestedValue1": "nestedValue1"
}
}
I am sceptical about there being a way to deserialize a json like that back to an object tho.
Update:
As some asked what is the reason I'm trying to accomplish this:
The reason I asked this question is because I serialize this object to send as json metadata to a service that only allows referencing top level propreties in a way similar to this:
[Metadata_Value1] would return "value1"
However [Metadata_NestedObject_NestedValue1] doesn't work and there isn't any indication to there being a way to reference nested properties.
Taking this in consideration I hoped there would be some solution that would allow keeping the nested objects in my program but transforming them all to top properties when sending them to this service.
In the service I would then be able to do: [NestedObject_NestedValue1] and get the value "nestedValue1"
You can utilize a custom converter that looks like below :
public class NestedJsonConverter : JsonConverter
{
private readonly Type[] _types;
public NestedJsonConverter(params Type[] types)
{
_types = types;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JToken t = JToken.FromObject(value);
if (t.Type != JTokenType.Object)
{
t.WriteTo(writer);
}
else
{
JObject o = (JObject)t;
writer.WriteStartObject();
void writeNested(JObject target, object source, string prefix)
{
target.Properties().ToList().ForEach(p =>
{
var prop = source.GetType().GetProperty(p.Name);
var value = prop.GetValue(source);
var prefixed = string.IsNullOrEmpty(prefix) ? p.Name : $"{prefix}_{p.Name}";
if (p.Value.Type == JTokenType.Object)
{
writeNested((JObject)p.Value, value, prefixed);
}
else if (p.Value.Type == JTokenType.Array)
{
// you may need a more advanced handling in array scenarios
var arr = (JArray)p.Value;
writer.WritePropertyName(prefixed);
arr.WriteTo(writer);
}
else
{
writer.WritePropertyName(prefixed);
writer.WriteValue(value);
}
}
);
}
writeNested(o, value, "");
writer.WriteEndObject();
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
}
public override bool CanRead
{
get
{
return false;
}
}
public override bool CanConvert(Type objectType)
{
return _types.Any(t => t == objectType);
}
}
Dotnetfiddle

Deserializing a list of dynamic properties

My application is consuming an API, and I'm trying to deserialize data of images coming back. The data is formatted like:
{
"images":{
"totalCount":4,
"0":{
"url":"file1.jpg"
},
"1":{
"url":"file2.jpg"
},
"2":{
"url":"file3.jpg"
},
"3":{
"url":"file4.jpg"
}
}
}
I have these model classes:
public class MyViewModel
{
[JsonProperty("images")]
public ImagesViewModel Images { get; set; }
}
public class ImagesViewModel
{
[JsonProperty("totalCount")]
public int TotalCount { get; set; }
public Dictionary<string, ImageViewModel> ListImages { get; set; }
}
public class ImageViewModel
{
[JsonProperty("url")]
public string Url { get; set; }
}
The collection of images isn't really a collection, for some reason it's just a new property for each image. I'm trying to deserialize my object like:
... // create HttpClient object, add headers and such
System.Net.Http.HttpResponseMessage response = await
client.GetAsync(endpointUrl);
var jsonString = response.Content.ReadAsStringAsync();
MyViewModel model =
JsonConvert.DeserializeObject<MyViewModel>(jsonString.Result);
I get back the totalCount property just fine, but the collection of images is coming back null.
Is there a way for me to change my view models so that I can deserialize the json correctly?
Given the formatting of the JSON you will have to go the long route and try to deserialize it using JObjects
//using Newtonsoft.Json.Linq
var jObject = JObject.Parse(jsonString);
var images = jObject.Property("images").Value<JObject>(); ;
var viewModel = new MyViewModel {
Images = new ImagesViewModel {
TotalCount = images.Property("totalCount").Value<int>(),
ListImages = images.Properties().Skip(1).ToDictionary(p => p.Name, p => p.Value<ImageViewModel>())
}
};
Going a step further and using a JsonConverter for converting the payload itself actually works as well given that we know now how to convert it.
public class MyViewModelConverter : JsonConverter {
public override bool CanConvert(Type objectType) {
return objectType == typeof(MyViewModel);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
var jObject = JObject.Load(reader);//<-- Note the use of Load() instead of Parse()
var images = jObject.Property("images").Value<JObject>(); ;
var model = new MyViewModel {
Images = new ImagesViewModel {
TotalCount = images.Property("totalCount").Value<int>(),
ListImages = images.Properties().Skip(1).ToDictionary(p => p.Name, p => p.Value<ImageViewModel>())
}
};
return model;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
throw new NotImplementedException();
}
}
and decorating the class itself
[JsonConverter(typeof(MyViewModelConverter))]
public class MyViewModel {
[JsonProperty("images")]
public ImagesViewModel Images { get; set; }
}
Deserialization is now as you intended to do before
var jsonString = await response.Content.ReadAsStringAsync();
MyViewModel model = JsonConvert.DeserializeObject<MyViewModel>(jsonString);
.NET Abhors dynamic types. They fly in the face of solid type checking at compile time. That being said, there is support for it:
As the example data is basically just a array of images, any collection could deal with this input.
If you can not even define the types umanbigiously (you might have a array of images and one of strings), the only way is ExpandoObject. It is designed specifically to deal with such cases. It is basically a List[string, object] with some Syntax Sugar, but it also does includes functions like Property Change Notifications.
Sounds like a job for a custom converter!
A custom converter will let you supply your own logic for deserializing specific types. Newtonsoft uses the target class to figure out with type if expects to find in the json and call the appropriate converter.
class ImagesViewModelConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(ImagesViewModel);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
assertToken(JsonToken.StartObject);
var obj = new ImagesViewModel()
{
ListImages = new Dictionary<string, ImageViewModel>()
};
while (reader.Read() && reader.TokenType != JsonToken.EndObject)
{
assertToken(JsonToken.PropertyName);
var propName = (string)reader.Value;
if (propName.Equals(nameof(ImagesViewModel.TotalCount), StringComparison.InvariantCultureIgnoreCase))
{
reader.Read();
assertToken(JsonToken.Integer);
obj.TotalCount = (int)((Int64)reader.Value);
continue;
}
reader.Read();
var image = serializer.Deserialize<ImageViewModel>(reader); // you can still use normal json deseralization inside a converter
obj.ListImages.Add(propName, image);
}
return obj;
void assertToken(JsonToken token)
{
if (reader.TokenType != token)
throw new Exception(); // might wanna add detailed errors
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException(); // implement if needed
}
}
And then:
var settings = new JsonSerializerSettings()
{
Converters = new[] { new ImagesViewModelConverter() }
};
var obj = JsonConvert.DeserializeObject<MyViewModel>(jsonString, settings);
});
You can even change classes to be easier to handle, given that they no longer need to match the json exactly. You can for example replace the dict with an array and have the converter fill it in order.

Peek JsonReader to read outside of JsonConverter's own object [duplicate]

I have a client which can call two different versions of a service.
One service only sends a single value:
{
"value" : { ... }
}
The second service always returns multiple values:
{
"values" : [
{ ... },
{ ... }
]
}
Ideally, I'd like to represent this with a single object in my client classes so the user never sees whether it's a single value or multiple values.
public class MyValues
{
public List<Stuff> Values { get; set; }
public Thing Other { get; set; }
}
I think that the only way I'll be able to accomplish this is with a custom JsonConverter class which I apply to MyValues, but I really only want to do something custom when I'm deserializing the property value. I can't seem to figure out if an IContractResolver would be a better way to go (e.g. somehow attach a phantom property to MyValues that deserializes value and puts it into Values.
If I create a custom converter, how to I tell it to deserialize everything else normally (e.g. if Other has an extra properties make sure they are handled appropriately, etc.)
Rather than writing a JsonConverter, you could make a set-only property Value on your MyValues, like so:
public class MyValues
{
[JsonProperty]
Stuff Value
{
set
{
(Values = Values ?? new List<Stuff>(1)).Clear();
Values.Add(value);
}
}
public List<Stuff> Values { get; set; }
public Thing Other { get; set; }
}
It could be public or private if marked with [JsonProperty]. In this case Json.NET will call the Value setter if the singleton "value" property is encountered in the JSON, and call the Values setter if the array "values" property is encountered. Since the property is set-only only the array property will be re-serialized.
To make a custom JsonConverter that has special processing for a few properties of a type but uses default processing for the remainder, you can load the JSON into a JObject, detach and process the custom properties, then populate the remainder from the JObject with JsonSerializer.Populate(), like so:
class MyValuesConverter : CustomPropertyConverterBase<MyValues>
{
protected override void ProcessCustomProperties(JObject obj, MyValues value, JsonSerializer serializer)
{
// Remove the value property for manual deserialization, and deserialize
var jValue = obj.GetValue("value", StringComparison.OrdinalIgnoreCase).RemoveFromLowestPossibleParent();
if (jValue != null)
{
(value.Values = value.Values ?? new List<Stuff>()).Clear();
value.Values.Add(jValue.ToObject<Stuff>(serializer));
}
}
}
public abstract class CustomPropertyConverterBase<T> : JsonConverter where T : class
{
public override bool CanConvert(Type objectType)
{
return typeof(T).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var jObj = JObject.Load(reader);
var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(objectType);
var value = existingValue as T ?? (T)contract.DefaultCreator();
ProcessCustomProperties(jObj, value, serializer);
// Populate the remaining properties.
using (var subReader = jObj.CreateReader())
{
serializer.Populate(subReader, value);
}
return value;
}
protected abstract void ProcessCustomProperties(JObject obj, T value, JsonSerializer serializer);
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
public static class JsonExtensions
{
public static JToken RemoveFromLowestPossibleParent(this JToken node)
{
if (node == null)
return null;
var contained = node.AncestorsAndSelf().Where(t => t.Parent is JContainer && t.Parent.Type != JTokenType.Property).FirstOrDefault();
if (contained != null)
contained.Remove();
// Also detach the node from its immediate containing property -- Remove() does not do this even though it seems like it should
if (node.Parent is JProperty)
((JProperty)node.Parent).Value = null;
return node;
}
}

How to return NULL from JsonConvert.DeserializeObject

I have a class:
public class CustomResponse
{
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string Message {get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string Details {get; set; }
}
Then I'm trying to deserialize JSON string to this class:
var settings = new JsonSerializerSettings
{
NullValueHandling.Ignore,
MissingMemberHandling.Ignore,
};
var customResponse = JsonConvert.Deserialize<CustomResponse>(jsonString, settings);
My JSON string for example:
{"DocumentId":"123321", "DocumentNumber":"ABC123"}
As a result I have an object which have all properties is NULL, but customResponse is not NULL. How do I get NULL in result?
If you want to avoid creating a custom JsonConverter, you can use the following extension method:
public static class Exts
{
public static T OrDefaultWhen<T>(this T obj, Func<T, bool> predicate)
{
return predicate(obj) ? default(T) : obj;
}
}
Usage:
var jsonString = #"{
Message: null,
Details: null
}";
var res = JsonConvert.DeserializeObject<CustomResponse>(jsonString)
.OrDefaultWhen(x => x.Details == null && x.Message == null);
You could create a custom JsonConverter that allocates and populates your object, then checks afterwards to see whether all properties are null, using the value returned by the JsonProperty.ValueProvider:
public class ReturnNullConverter<T> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(T).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
// Get the contract.
var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(objectType);
// Allocate the object (it must have a parameterless constructor)
existingValue = existingValue ?? contract.DefaultCreator();
// Populate the values.
serializer.Populate(reader, existingValue);
// This checks for all properties being null. Value types are never null, however the question
// doesn't specify a requirement in such a case. An alternative would be to check equality
// with p.DefaultValue
// http://www.newtonsoft.com/json/help/html/P_Newtonsoft_Json_Serialization_JsonProperty_DefaultValue.htm
if (contract.Properties
.Where(p => p.Readable)
.All(p => object.ReferenceEquals(p.ValueProvider.GetValue(existingValue), null)))
return null;
return existingValue;
}
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Then use it like:
var settings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
MissingMemberHandling = MissingMemberHandling.Ignore,
Converters = { new ReturnNullConverter<CustomResponse>() },
};
var customResponse = JsonConvert.DeserializeObject<CustomResponse>(jsonString, settings);
Sample fiddle.
However, in comments you wrote, if CustomResponse is NULL, then service return a correct response and i try to deserialize to OtherCustomResponse. In that case, consider using a polymorphic converter such as the ones from How to implement custom JsonConverter in JSON.NET to deserialize a List of base class objects? or Deserializing polymorphic json classes without type information using json.net

Categories