Prevent serialization if value is null or whitespace in JSON.NET - c#

I have an object that needs to be serialized in such a way that both null and "whitespace" (empty or just spaces) values are not serialized. I don't control the object itself and therefore can't set attributes, but I know that all the properties are strings. Setting NullValueHandling to Ignore obviously only gets me part of the way to the solution.
It "seems" (as best I understand) like what I need to do is create a custom DefaultContractResolver but I haven't come up with a solution that works. Here are a couple failed attempts, for reference, that throw no exceptions but have no obvious effect on the serialization either:
public class NoNullWhiteSpaceResolver : DefaultContractResolver
{
public static readonly NoNullWhiteSpaceResolver Instance = new NoNullWhiteSpaceResolver();
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
/* this doesn't work either
if (property.ValueProvider.GetValue(member) == null ||
(property.PropertyType == typeof(string) &&
string.IsNullOrWhiteSpace((string)property.ValueProvider.GetValue(member))))
{
property.ShouldSerialize = i => false;
}*/
if (property.PropertyType == typeof(string))
{
property.ShouldSerialize =
instance =>
{
try
{
string s = (string) instance;
bool shouldSkip = string.IsNullOrWhiteSpace(s);
return !string.IsNullOrWhiteSpace(s);
}
catch
{
return true;
}
};
}
return property;
}
}
I'm implementing the resolver by
string str = JsonConvert.SerializeObject(obj, new JsonSerializerSettings
{
Formatting = Formatting.None;
ContractResolver = new NoNullWhiteSpaceResolver();
});
Maybe I'm going about this backward but I appreciate any insights people have. I've worked around the issue by using an extension method/reflection to iterate over the properties of the object and setting the value to null if it's "nullorwhitespace" and then using the standard NullValueHandling but I'm hoping I can find a way to configure all of this in the serialization.

This seems to work:
public class NoNullWhiteSpaceResolver : DefaultContractResolver {
public static readonly NoNullWhiteSpaceResolver Instance = new NoNullWhiteSpaceResolver();
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) {
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (property.PropertyType == typeof(string)) {
property.ShouldSerialize =
instance => {
try {
var rawValue = property.ValueProvider.GetValue(instance);
if (rawValue == null) {
return false;
}
string stringValue = property.ValueProvider.GetValue(instance).ToString();
return !string.IsNullOrWhiteSpace(stringValue);
}
catch {
return true;
}
};
}
return property;
}
}
Using this test class:
public class TestClass {
public string WhiteSpace => " ";
public string Null = null;
public string Empty = string.Empty;
public string Value = "value";
}
This is the output:
{"Value":"value"}

Related

Removing User Object Property

I am trying to remove a property within a Microsoft.graph User object.
I am currently using the "Property.Remove()" method. First, I am setting the User object to a deserialized JObject.
The way I am doing this is serializing the User object into a Json string (jsonString) and deserializing jsonString (jsonStringUpdated). Afterwards, I am calling jsonStringUpdated.property("____") .Remove();
Is there a simpler way to do this? Below is an example:
{
string jsonString = JsonConvert.SerializeObject(user);
var jsonStringUpdated = (JObject)JsonConvert.DeserializeObject(jsonString);
jsonStringUpdated.Property("country").Remove();
}
I would not say that this is simpler option, but I would say that it is better one (in terms of reusability, flexibility and possibly performance). You can create custom contract resolver:
// generic type parameter can be omitted with hardcoding typeof
// handling for multiple properties can be added
public class IgnorePropertyResolver<T> : DefaultContractResolver
{
private readonly string _propName;
public IgnorePropertyResolver(string propName)
{
_propName = propName;
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
if (typeof(T).IsAssignableFrom(member.DeclaringType) && member.Name == _propName)
{
property.ShouldSerialize = o => false;
}
return property;
}
}
And usage:
class MyClass
{
public int Prop1 { get; set; }
public int Prop2 { get; set; }
}
var serializeObject = JsonConvert.SerializeObject(new MyClass(),
new JsonSerializerSettings
{
ContractResolver = new IgnorePropertyResolver<MyClass>(nameof(MyClass.Prop2))
});
Console.WriteLine(serializeObject); // prints {"Prop1":0}

How to Ignore Some DataMember on Runtime JSON

//i want some data members to serialize not all based on some condition.
class Days
{
}
class Weeks
{
}
class Months
{
List<Days> listDays=new List<Days>();
List<Months > listMonths=new List<Months >();
}
class Year
{
Months m=new Months();
if(day=="monday")
{
listDays.add(day)
}
}
class Year
{
Months m=new Months();
if(day=="monday")
{
listDays.add(day)
}
i dont want to empty here months that i not used so i want to remove when json created
{ "listMonths": [],"listDays": [ "Monday"]}
"listMonths": [] give me some solution so i can neglate unnecessary data member while serialization.
you can try the overloaded method of SerializeObject to exclude null objects. something like below
JsonConvert.SerializeObject(jSONDocument,
Newtonsoft.Json.Formatting.None,
new JsonSerializerSettings {
NullValueHandling = NullValueHandling.Ignore
});
Unfortunately, there is no serializer option to ignore empty collections. however, you can use DefaultContractResolver to ignore empty collection
public class EmptyCollectionContractResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
Predicate<object> shouldSerialize = property.ShouldSerialize;
property.ShouldSerialize = obj => (shouldSerialize == null || shouldSerialize(obj)) && !IsEmptyCollection(property, obj);
return property;
}
private bool IsEmptyCollection(JsonProperty property, object target)
{
var value = property.ValueProvider.GetValue(target);
var collection = value as ICollection;
if (collection != null && collection.Count == 0)
return true;
if (!typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
return false;
var countProp = property.PropertyType.GetProperty("Count");
if (countProp == null)
return false;
var count = (int)countProp.GetValue(value, null);
return count == 0;
}
}
And change SerializeObject to
json = JsonConvert.SerializeObject(jSONDocument,
Newtonsoft.Json.Formatting.None,
new JsonSerializerSettings
{
ContractResolver = new EmptyCollectionContractResolver(),
NullValueHandling = NullValueHandling.Ignore
});

How can I solve "Required property not found in json" error without changing the json and its corresponding class?

A)
I have a DLL library include a class like the bellow code:
public class Thing
{
// OTHER_PROPERTIES
//..........................
[JsonProperty(Required = Required.Always)]
public bool IsBook { get; set; }
}
Assumption: I have not access to the source of the Thing class.
B)
And I have a JSON like this:
{
OTHER_PROPERTIES
}
Note: It has not a pair of name-value for IsBook
Assumption: I wont change the JSON to include IsBook in it.
C)
If I run JsonConvert.DeserializeObject<Thing>(json); then I get the following error:
Error: Required property not found in json ....
Question:
How can I write a custom code to solve the above error by observing the assumptions of the problem?
(please read the above assumptions again before writing your answer.)
You can use custom contract resolver to achieve that. For example:
public class Thing
{
[JsonProperty("Name")]
public string Name { get; set; }
[JsonProperty(Required = Required.Always)]
public bool IsBook { get; set; }
}
class NeverRequiredContractResolver : DefaultContractResolver {
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) {
var prop = base.CreateProperty(member, memberSerialization);
prop.Required = Required.Default;
return prop;
}
}
And then:
var test = JsonConvert.DeserializeObject<Thing>("{\"Name\":\"some name\"}",
new JsonSerializerSettings {
ContractResolver = new NeverRequiredContractResolver()
});
Of course you can assign resolver globally and not pass it to every DeserializeObject:
JsonConvert.DefaultSettings = () => new JsonSerializerSettings()
{
ContractResolver = new NeverRequiredContractResolver()
};
var test = JsonConvert.DeserializeObject<Thing>("{\"Name\":\"some name\"}");
you can make only specific properties of specific classes non-required (and not all of them like in example above):
class CustomPropertyContractResolver : DefaultContractResolver {
private readonly Action<MemberInfo, JsonProperty> _propFixup;
public CustomPropertyContractResolver(Action<MemberInfo, JsonProperty> propFixup) {
_propFixup = propFixup;
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) {
var prop = base.CreateProperty(member, memberSerialization);
_propFixup?.Invoke(member , prop);
return prop;
}
}
JsonConvert.DefaultSettings = () => new JsonSerializerSettings()
{
ContractResolver = new CustomPropertyContractResolver((member, prop) => {
if (member.DeclaringType == typeof(Thing) && member.Name == "IsBook") {
prop.Required = Required.Default;
}
})
};
var test = JsonConvert.DeserializeObject<Thing>("{\"Name\":\"some name\"}");
and overall adjust example above however you wish for your specific use case.

Replace sensitive data value on JSON serialization

I have some objects that I want to serialize as JSON. However, some of the objects have properties which are deemed 'SensitiveData' via attributes.
[SensitiveDataAttribute]
public string SomeSensitiveProperty {get; set;}
At the moment, I am overriding the 'CreateProperty' method on the serializer so that I can alter whether or not a property should be serialized dependent upon whether it has this 'SensitiveData' attribute:
public class SensitiveDataResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
property.ShouldSerialize = instance =>
{
if (member is PropertyInfo)
{
var prop = (PropertyInfo) member;
var isSensitiveData = Attribute.IsDefined(prop, typeof (SensitiveDataAttribute));
return !isSensitiveData;
}
return false;
};
return property;
}
}
}
When I serialize, I then use that resolver as settings for the serializer:
var settings = new JsonSerializerSettings() { ContractResolver = new SensitiveDataResolver() };
var requestString = JsonConvert.SerializeObject(someObject, settings);
My problem is, I don't want the properties to be excluded from the serialization. I want them to be serialized but with the default value 'SensitiveData' set against them.
Is there a way I can achieve this using Attributes?
Instead of using the ShouldSerialize method, you can just override the property value if the member has the attribute. To do this, you need to supply a custom IValueProvider for Json.NET to use when serializing.
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
if (member is PropertyInfo)
{
var prop = (PropertyInfo)member;
var isSensitiveData = Attribute.IsDefined(prop, typeof (SensitiveDataAttribute));
if (isSensitiveData)
property.ValueProvider = new StringValueProvider("SensitiveData");
}
return property;
}
StringValueProvider is a custom implementation of the IValueProvider interface.
public class StringValueProvider : IValueProvider
{
private readonly string _value;
public StringValueProvider(string value)
{
_value = value;
}
public void SetValue(object target, object value)
{
throw new NotSupportedException();
}
public object GetValue(object target)
{
return _value;
}
}
Implement IValueProvider for sensitive data property and use it for PropertyValueProvider.
public class SensitiveDataProvider : IValueProvider
{
readonly string sesitiveDatatag = "Sensitive Data";
public object GetValue(object target)
{
return sesitiveDatatag;
}
public void SetValue(object target, object value)
{
target = sesitiveDatatag;
}
}
Now your DefaultContractResolver would be:
public class SensitiveDataResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
if (member is PropertyInfo)
{
var prop = (PropertyInfo)member;
var isSensitiveData = Attribute.IsDefined(prop, typeof(SensitiveDataAttribute));
if(isSensitiveData)
{
property.ValueProvider = new SensitiveDataProvider();
}
}
return property;
}
}
Instead of setting property.ShouldSerialize you can overwrite the property value itself.
public class SensitiveDataResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
if (this.IsSensitiveProperty(member))
{
((PropertyInfo)member).SetValue(member, "SensitiveData", null);
}
var property = base.CreateProperty(member, memberSerialization);
return property;
}
private bool IsSensitiveProperty(MemberInfo member)
{
if (member is PropertyInfo)
{
var prop = (PropertyInfo) member;
var isSensitiveData = Attribute.IsDefined(prop, typeof (SensitiveDataAttribute));
return isSensitiveData;
}
return false;
}
}
You could do this with reflection inside of your contract resolver.
PropertyInfo[] props = typeof(instanceOfMyClass).GetProperties();
foreach (PropertyInfo prop in props)
{
object[] attrs = prop.GetCustomAttributes(true);
foreach (object attr in attrs)
{
var sensitive = attr as SensitiveDataAttribute;
if (sensitive != null)
{
//add the default value to your property here
prop.SetValue(instanceOfMyClass, "Default Value", null);
}
}
}

Refactor of ShouldSerialize () in class... can I use IContractResolver?

I have an API that returns a big list of car features.... all are either bool or ints... and basically I only want to display the ones that return true values or >0 for the ints.
I am using JSON.net so that I san use the ShouldSerialize() property to determine if I should serialize the property based upon its value and my code looks like this:
public class Features
{
public bool ABS { get; set; }
public bool ShouldSerializeABS()
{
// don't serialize the ABS property if ABS is false
return (ABS != false);
}
public bool Immobiliser { get; set; }
public bool ShouldSerializeImmobiliser ()
{
// don't serialize the Immobiliser property if Immobiliser is false
return (Immobiliser != false);
}
public int BHP { get; set; }
public bool ShouldSerializeBHP ()
{
// don't serialize the BHP property if BHP is false
return (BHP != 0);
}
//..... etc
}
This works great and gives me the results I am after, however I was just wondering if there is a way to re-factor this so that my class does not become cluttered with all the ShouldSerialize() properties?
I have been looking into CopyConditional properties with IContractResolver on http://james.newtonking.com/projects/json/help/index.html?topic=html/ConditionalProperties.htm and looks like it might be possible to use IContractResolver for such a purpose, but I still seem to end up with lots of code that does not seem to re-factor out
public class ShouldSerializeContractResolver : DefaultContractResolver
{
public new static readonly ShouldSerializeContractResolver Instance = new ShouldSerializeContractResolver();
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (property.DeclaringType == typeof(Features) && property.PropertyName == "ABS")
{
property.ShouldSerialize =
instance =>
{
Features e = (Features)instance;
return e.ABS != false;
};
}
if (property.DeclaringType == typeof(Features) && property.PropertyName == "Immobiliser")
{
property.ShouldSerialize =
instance =>
{
Features e = (Features)instance;
return e.Immobiliser != false;
};
}
return property;
}
}
and this method using the ShouldSerializeContractResolver does not seem to remove the property from the class if it is false... any help is greatly appreciated
It sounds like what you are trying to accomplish by writing all these ShouldSerialize() methods can be accomplished by just changing the DefaultValueHandling setting on the serializer to Ignore. This will cause any values that are equal to their default values (false for bool, 0 for int) not to be serialized.
JsonSerializerSettings jsonSettings = new JsonSerializerSettings();
jsonSettings.DefaultValueHandling = DefaultValueHandling.Ignore;
string json = JsonConvert.SerializeObject(yourObject, jsonSettings);
If you're using Web API, then you can access the settings of the Json.NET serializer via the Register method of the WebApiConfig class (in the App_Start folder).
JsonSerializerSettings settings = config.Formatters.JsonFormatter.SerializerSettings;
settings.DefaultValueHandling = DefaultValueHandling.Ignore;

Categories