Replace sensitive data value on JSON serialization - c#

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);
}
}
}

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}

Newtonsoft.Json DefaultContractResolver remove key from dictionary

I have the following class (I don't want to change the class to solve the issue..):
public class Test
{
public Dictionary<string, string> Data;
[PrivateField]
[JsonIgnore]
public string Name { get { return Data["Name"]; } set { Data.Add("Name", value); } }
public string NotPrivate { get { return Data["NotPrivate"]; } set { Data.Add("NotPrivate", value); } }
}
I want to remove specific key from Data property during serialize, in my case the key in the dictionary is 'Name' because it is marked as private.
var test = new Test();
var settings = new JsonSerializerSettings();
settings.ContractResolver = new IgnorePrivatePropertiesContractResolver();
var places = JsonConvert.SerializeObject(test, settings);
public class IgnorePrivatePropertiesContractResolver : DefaultContractResolver
in IgnorePrivatePropertiesContractResolver I have tried :
override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
but I can't get the Dictionary out of JsonProperty.
I also tried :
JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
but I can't get the Dictionary out of MemberInfo .
Json.NET has no built-in functionality to filter keys from dictionaries while serializing. Therefore you will need to create a custom JsonConverter that does the necessary filtering. Then, since you can't change your class, you will need to apply the converter using a custom contract resolver.
First, the custom JsonConverter:
public class KeyFilteringDictionaryConverter<TKey, TValue> : JsonConverter<IDictionary<TKey, TValue>>
{
readonly HashSet<TKey> toSkip;
public KeyFilteringDictionaryConverter(IEnumerable<TKey> toSkip) => this.toSkip = toSkip?.ToHashSet() ?? throw new ArgumentNullException(nameof(toSkip));
public override void WriteJson(JsonWriter writer, IDictionary<TKey, TValue> value, JsonSerializer serializer) => serializer.Serialize(writer, new KeyFilteringDictionarySurrogate<TKey, TValue>(value, toSkip));
public override bool CanRead => false;
public override IDictionary<TKey, TValue> ReadJson(JsonReader reader, Type objectType, IDictionary<TKey, TValue> existingValue, bool hasExistingValue, JsonSerializer serializer) => throw new NotImplementedException();
}
public class KeyFilteringDictionarySurrogate<TKey, TValue> : IReadOnlyDictionary<TKey, TValue>
{
readonly IDictionary<TKey, TValue> dictionary;
readonly HashSet<TKey> toSkip;
public KeyFilteringDictionarySurrogate(IDictionary<TKey, TValue> dictionary, IEnumerable<TKey> toSkip) : this(dictionary, toSkip ?.ToHashSet()) { }
public KeyFilteringDictionarySurrogate(IDictionary<TKey, TValue> dictionary, HashSet<TKey> toSkip)
{
this.dictionary = dictionary ?? throw new ArgumentNullException(nameof(dictionary));
this.toSkip = toSkip ?? throw new ArgumentNullException(nameof(toSkip));
}
public bool ContainsKey(TKey key) => !toSkip.Contains(key) && dictionary.ContainsKey(key);
public bool TryGetValue(TKey key, out TValue value)
{
if (toSkip.Contains(key))
{
value = default(TValue);
return false;
}
return dictionary.TryGetValue(key, out value);
}
public TValue this[TKey key] => toSkip.Contains(key) ? throw new KeyNotFoundException() : dictionary[key];
public IEnumerable<TKey> Keys => this.Select(p => p.Key);
public IEnumerable<TValue> Values => this.Select(p => p.Value);
public int Count => this.Count(); // Could be made faster?
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => dictionary.Where(p => !toSkip.Contains(p.Key)).GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
Next, the custom ContractResolver. The easiest place to apply the custom converter would be in CreateProperties() after all the property information has been created. Overriding CreateObjectContract() would also work.
public class IgnorePrivatePropertiesContractResolver : DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var jsonProperties = base.CreateProperties(type, memberSerialization);
// Apply to all string-keyed dictionary properties named "Data" that do not already have a converter
foreach (var dataProperty in jsonProperties.Where(p => p.PropertyName == "Data" && p.Converter == null))
{
var keyValuePairTypes = dataProperty.PropertyType.GetDictionaryKeyValueTypes().ToList();
if (keyValuePairTypes.Count == 1 && keyValuePairTypes[0][0] == typeof(string))
{
// Filter all properties with PrivateFieldAttribute applied
var ignoreProperties = jsonProperties.Where(p => p.AttributeProvider.GetAttributes(typeof(PrivateFieldAttribute), true).Any()).Select(p => p.PropertyName).ToHashSet();
if (ignoreProperties.Count > 0)
{
dataProperty.Converter = (JsonConverter)Activator.CreateInstance(typeof(KeyFilteringDictionaryConverter<,>).MakeGenericType(keyValuePairTypes[0]), new object [] { ignoreProperties });
}
}
}
return jsonProperties;
}
}
public static class TypeExtensions
{
public static IEnumerable<Type> GetInterfacesAndSelf(this Type type)
=> (type ?? throw new ArgumentNullException()).IsInterface ? new[] { type }.Concat(type.GetInterfaces()) : type.GetInterfaces();
public static IEnumerable<Type []> GetDictionaryKeyValueTypes(this Type type)
=> type.GetInterfacesAndSelf().Where(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IDictionary<,>)).Select(t => t.GetGenericArguments());
}
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple=false)]
public class PrivateFieldAttribute : System.Attribute { }
Finally, use e.g. as follows:
var test = new Test
{
Data = new Dictionary<string, string>
{
{"SomeAdditionalKey", "Some additional value"},
},
Name = "private value",
NotPrivate = "public value",
};
var resolver = new IgnorePrivatePropertiesContractResolver();
var settings = new JsonSerializerSettings
{
ContractResolver = resolver,
};
var json = JsonConvert.SerializeObject(test, Formatting.Indented, settings);
Notes:
This solution doesn't actually remove the private key from the dictionary, it simply skips serializing it. Generally serialization doesn't actually modify the object being serialized. If you really need to actually modify the dictionary during serialization your contract resolver could apply a custom OnSerializing() method to do that, instead of applying a converter.
Your question doesn't specify exactly how to determine the properties to which KeyFilteringDictionaryConverter should be applied. I applied it to all Dictionary<string, TValue> properties named "Data" when there is also a member with PrivateFieldAttribute in the same class. You could restrict it to just Test, or use any other logic that suits your needs, as long as the converter is applied only to a property of type IDictionary<string, TValue> for any type TValue.
You may want to statically cache the contract resolver for best performance.
The converter does not attempt to remove private properties while deserializing as this was not required by your question.
Your Test class, when serialized, includes the value of NotPrivate twice: once as a property, and once as a member of the dictionary property:
{
"Data": {
"SomeAdditionalKey": "Some additional value",
"NotPrivate": "public value"
},
"NotPrivate": "public value"
}
You may want to exclude it from one location or the other.
You wrote, I can't get the Dictionary out of JsonProperty.
That is correct. A contract resolver defines how to map .Net types to JSON. As such, specific instances are not available during contract creation. That's why it is necessary to apply a custom converter, as specific instances are passed into the converter during serialization and deserialization.
Demo fiddle here.
public class IgnorePrivatePropertiesContractResolver : DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
if (!(type.Name.Equals(typeof(Test).Name)))
{
return properties;
}
var dataProperty = properties.FirstOrDefault(prop => prop.PropertyName.Equals("Data"));
if (dataProperty == null)
return properties;
var privatePropertiesNames = properties.Where(prop => prop.AttributeProvider.GetAttributes(false).OfType<PrivateFieldAttribute>().Any())
.Select(privateProperty => privateProperty.PropertyName);
dataProperty.ValueProvider = new JsonPropertyExtraDataValueProvider(privatePropertiesNames);
return properties;
}
}
public class JsonPropertyExtraDataValueProvider : IValueProvider
{
private IEnumerable<string> _privateKeys;
public JsonPropertyExtraDataValueProvider(IEnumerable<string> privateKeys)
{
_privateKeys = privateKeys;
}
public void SetValue(object target, object value)
{
throw new NotImplementedException();
}
public object GetValue(object target)
{
Dictionary<string, string> value = ((Test)target).ExtraData.ToDictionary(pr => pr.Key, pr => pr.Value);
foreach (var privateKey in _privateKeys)
value.Remove(privateKey);
return value;
}
}

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

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"}

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.

Json Convert empty string instead of null

I'm trying to serialize my struct so that the strings that didn't get a value get their default value "" instead of null
[JsonProperty(PropertyName = "myProperty", DefaultValueHandling = DefaultValueHandling.Populate)]
[DefaultValue("")]
public string MyProperty{ get; set; }
My result in the Json string:
"myProperty": null,
what i want
"myProperty": "",
I also tried creating a converter without any effect, the can Convert and WriteJson functions aren't even firing for some reason:
[JsonProperty(PropertyName = "myProperty")]
[JsonConverter(typeof(NullToEmptyStringConverter))]
public string MyProperty{ get; set; }
class NullToEmptyStringConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(object[]);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value == null)
writer.WriteValue("");
}
}
This isnt helping either Json.Net How to deserialize null as empty string?
This should work:
var settings = new JsonSerializerSettings() { ContractResolver= new NullToEmptyStringResolver() };
var str = JsonConvert.SerializeObject(yourObj, settings);
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System.Reflection;
public class NullToEmptyStringResolver : Newtonsoft.Json.Serialization.DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
return type.GetProperties()
.Select(p=>{
var jp = base.CreateProperty(p, memberSerialization);
jp.ValueProvider = new NullToEmptyStringValueProvider(p);
return jp;
}).ToList();
}
}
public class NullToEmptyStringValueProvider : IValueProvider
{
PropertyInfo _MemberInfo;
public NullToEmptyStringValueProvider(PropertyInfo memberInfo)
{
_MemberInfo = memberInfo;
}
public object GetValue(object target)
{
object result = _MemberInfo.GetValue(target);
if (_MemberInfo.PropertyType == typeof(string) && result == null) result = "";
return result;
}
public void SetValue(object target, object value)
{
_MemberInfo.SetValue(target, value);
}
}
While the accepted answer pointed me in the right direction, it appears quite brittle. I do not want to worry about resolving the list of JsonProperty objects and implementing IValueResolver myself when there are perfectly functional tools available for doing that in Json.NET (which could have all kinds of optimizations and corner case handling built in that a basic reflection-based reimplementation won't).
My solution performs minimal overriding and resolver substitution to ensure that only parts that absolutely need to change are actually altered:
public sealed class SubstituteNullWithEmptyStringContractResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (property.PropertyType == typeof(string))
{
// Wrap value provider supplied by Json.NET.
property.ValueProvider = new NullToEmptyStringValueProvider(property.ValueProvider);
}
return property;
}
sealed class NullToEmptyStringValueProvider : IValueProvider
{
private readonly IValueProvider Provider;
public NullToEmptyStringValueProvider(IValueProvider provider)
{
if (provider == null) throw new ArgumentNullException("provider");
Provider = provider;
}
public object GetValue(object target)
{
return Provider.GetValue(target) ?? "";
}
public void SetValue(object target, object value)
{
Provider.SetValue(target, value);
}
}
}
Well, my solution pretty simple, but does not use JSON.NET features, just add backend field to your property:
public class Test
{
private string _myProperty = string.Empty;
[JsonProperty(PropertyName = "myProperty")]
public string MyProperty
{
get { return _myProperty; }
set { _myProperty = value; }
}
}
Edit:
In c# 6.0 property initialization will be available:
public class Test
{
[JsonProperty(PropertyName = "myProperty")]
public string MyProperty { get; set;} = "";
}
#Kirill Shlenskiy's solution is great, but it does not take the NullValueHandling attribute in consideration.
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string Remark{ get; set; }
Here is an improved version that will take care of it. If NullValueHandling.Ignore is set and the value is null, it will be skipped in the JSON output.
public sealed class SubstituteNullWithEmptyStringContractResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (property.PropertyType == typeof(string))
{
// Wrap value provider supplied by Json.NET.
property.ValueProvider = new NullToEmptyStringValueProvider(property.ValueProvider, property.NullValueHandling);
}
return property;
}
sealed class NullToEmptyStringValueProvider : IValueProvider
{
private readonly IValueProvider Provider;
private readonly NullValueHandling? NullHandling;
public NullToEmptyStringValueProvider(IValueProvider provider, NullValueHandling? nullValueHandling)
{
Provider = provider ?? throw new ArgumentNullException("provider");
NullHandling = nullValueHandling;
}
public object GetValue(object target)
{
if (NullHandling.HasValue
&& NullHandling.Value == NullValueHandling.Ignore
&& Provider.GetValue(target) == null )
{
return null;
}
return Provider.GetValue(target) ?? "";
}
public void SetValue(object target, object value)
{
Provider.SetValue(target, value);
}
}
}
An alternate solution (and perhaps a little cleaner). You can create your own JsonConverter class
class JsonNullToEmptyStringConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return existingValue ?? string.Empty;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(value ?? string.Empty);
}
}
Once this has been written up, you can tack it on to your property as an attribute:
[JsonConverter(typeof(JsonNullToEmptyStringConverter))]
public string CommentType { get; set; }
Original class is not mine. Thanks in advance to the many like you who contribute!
I added and solved null problems.
Public Class JsonBooleanConverter
Inherits JsonConverter
Public Status As String
Public ErrorCode As String
<JsonProperty(NullValueHandling:=NullValueHandling.Ignore)>
Public ErrorMessage As String
Public Overrides ReadOnly Property CanWrite As Boolean
Get
Return False
End Get
End Property
Public Overrides Sub WriteJson(ByVal writer As JsonWriter, ByVal value As Object, ByVal serializer As JsonSerializer)
Throw New NotImplementedException()
End Sub
Public Overrides Function ReadJson(ByVal reader As JsonReader, ByVal objectType As Type, ByVal existingValue As Object, ByVal serializer As JsonSerializer) As Object
If IsNothing(reader.Value) Then
Return If(existingValue, String.Empty)
End If
Dim value = reader.Value.ToString().ToLower().Trim()
If objectType = GetType(Boolean) Then
Select Case value
Case "true", "yes", "y", "1"
Return True
Case Else
Return False
End Select
ElseIf objectType = GetType(DateTime) Then
Return If(existingValue, String.Empty)
End If
Return If(existingValue, String.Empty)
'Return False
End Function
Public Overrides Function CanConvert(ByVal objectType As Type) As Boolean
If objectType = GetType(Boolean) Then
Return True
ElseIf objectType = GetType(DateTime) Then
Return True
End If
Return False
End Function
End Class
USAGE:
Dim listObjs As List(Of YourClass) = JsonConvert.DeserializeObject(Of List(Of YourClass))(responseFromServer, New JsonBooleanConverter())
Or:
Dim listObjs As YourClass= JsonConvert.DeserializeObject(Of YourClass)(responseFromServer, New JsonBooleanConverter())
With System.Text.Json and .NET Core 3.0 this worked for me:
var jsonSerializerOptions = new JsonSerializerOptions()
{
IgnoreNullValues = true
};
var myJson = JsonSerializer.Serialize(myObject,
jsonSerializerOptions );
using .NET 6 this was the solution:
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
HERE https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-ignore-properties?pivots=dotnet-6-0
with Newtonsoft
https://www.newtonsoft.com/json/help/html/NullValueHandlingIgnore.htm
,
Formatting.Indented, new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore
}
Person person = new Person
{
Name = "Nigal Newborn",
Age = 1
};
string jsonIncludeNullValues = JsonConvert.SerializeObject(person, Formatting.Indented);
Console.WriteLine(jsonIncludeNullValues);
// {
// "Name": "Nigal Newborn",
// "Age": 1,
// "Partner": null,
// "Salary": null
// }
string jsonIgnoreNullValues = JsonConvert.SerializeObject(person,
Formatting.Indented, new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore
} );
Console.WriteLine(jsonIgnoreNullValues);
// {
// "Name": "Nigal Newborn",
// "Age": 1
// }

Categories