49

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?

Community
  • 1
  • 1
Igor Meszaros
  • 2,081
  • 2
  • 22
  • 46
  • possible duplicate of [Serializing null in JSON.NET](http://stackoverflow.com/questions/8833961/serializing-null-in-json-net) – Christian Phillips May 23 '14 at 13:17
  • Already tried that, and its not working either – Igor Meszaros May 23 '14 at 13:27
  • 1
    The point of the `DefaultValue` attribute is not to *give* the property a default value. It is only meant as a signal to serialization that if the property has that value at the time of serialization, it doesn't need to be serialized since *it will get that value by default*. If you don't actually give that property that value by default then you are in fact abusing that attribute. – Lasse V. Karlsen Feb 25 '16 at 07:22

7 Answers7

44

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);
    }
}
College Code
  • 907
  • 6
  • 12
L.B
  • 114,136
  • 19
  • 178
  • 224
  • I have another contract resolver already set. There doesn't seem to be a way to have more than one – Joel Dean Aug 08 '15 at 23:32
  • 3
    @khellang If you want to make that much changes, please provide another answer, don't edit this one. I rolled it back. – L.B Sep 06 '16 at 19:54
  • 1
    I had a situation where needed multiple `ContractResolvers`. There is no option to have multiple resolvers but you can inherit from others. Here is a good example https://stackoverflow.com/questions/39612636/add-multiple-contract-resolver-in-newtonsoft-json For example, after applying `NullToEmptyStringResolver`, all JSON responses had lowercase field names. Inhering that from `CamelCasePropertyNamesContractResolver` worked well for me. – T.Nylund Aug 01 '19 at 17:10
  • 1
    This solution broke several other items in my object being serialized. Consider using Kirill Shlenski's solution below – Tyler Findlay Sep 11 '19 at 17:24
  • @L.B it doesn't work with nullable int – Vineet Agarwal Jun 20 '23 at 14:37
31

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);
        }
    }
}
Kirill Shlenskiy
  • 9,367
  • 27
  • 39
24

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;} = "";
}
Uriil
  • 11,948
  • 11
  • 47
  • 68
  • 1
    I would avoid doing this, since i have over 20 properties. But if all else falls I think I won't have another choice.. Thanks for your reply! – Igor Meszaros May 23 '14 at 13:29
  • Iv'e just tried this in an application I'm working on, and if you set the value using a mapper (In my case ValueInjecter) then this doesn't work as intended. – shawty Apr 07 '17 at 12:30
  • 5
    Nothing preventing you from calling MyProperty = null; thus back to a null string. – Gilbert Mar 26 '18 at 21:20
  • @Gilbert I had not thought of that. Thanks – Nico Oct 28 '22 at 18:48
8

@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);
        }
    }
}
Tony
  • 1,827
  • 1
  • 22
  • 23
1

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; }
Wassim Katbey
  • 298
  • 4
  • 9
1

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())
misleadingTitle
  • 657
  • 6
  • 21
-1

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
// }
Valentin Petkov
  • 1,570
  • 18
  • 23
  • This solution does not have the requested output of "Partner" being "" when the value is "empty" – Nico Oct 27 '22 at 19:50