22

I have a class. It has several properties lets say 10. Out of these 10, 3 are filled with data remaining 7 are blank.i.e. empty strings "" Used this link as reference. I would like only NON-NULL and NON-EMPTY string properties to be shown. But the end output has all 10 properties. I want only to see 3.

namespace Mynamespace.ValueObjects
{
[DataContract]
public class User
{
      [DataMember(Name ="userID", IsRequired = false,EmitDefaultValue = false)]
    public string userID { get; set; }
      [DataMember(Name ="ssn", IsRequired = false,EmitDefaultValue = false)]
    public string ssn { get; set; }
      [DataMember(Name ="empID", IsRequired = false,EmitDefaultValue = false)]
    public string empID { get; set; }
      [DataMember(Name ="schemaAgencyName", IsRequired = false,EmitDefaultValue = false)]
    public string schemaAgencyName { get; set; }
      [DataMember(Name ="givenName", IsRequired = false,EmitDefaultValue = false)]
    public string givenName { get; set; }
      [DataMember(Name ="familyName", IsRequired = false,EmitDefaultValue = false)]
    public string familyName { get; set; }
      [DataMember(Name ="password", IsRequired = false,EmitDefaultValue = false)]
    public string password { get; set; }
      ....

}

}

I also tried with

 [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]

as the attribute too. No luck. I also did like this

 var t = JsonConvert.SerializeObject(usr, Newtonsoft.Json.Formatting.None,
                                                new JsonSerializerSettings
                                                    {NullValueHandling = NullValueHandling.Ignore});

where 'usr' is the User instance. By no luck I mean, the 't' comes back with all the 10 properties

{"userID":"vick187","ssn":"","empID":"","schemaAgencyName":"","givenName":"","familyName":"","password":"pwd1234",...}

So as you can see only userID and password are populated. But I have ssn, empID etc still showing up. I only want userID and password. Any help would be appreciated.

Community
  • 1
  • 1
RookieAppler
  • 1,517
  • 5
  • 22
  • 58
  • I tried your code and I can confirm its working as expected. I got `{"userID":"vick187"}` all other properties were `null` before the serialization . You should have another piece of code that's messing up. What is `usr` look like before the serialization? – Kos Dec 22 '16 at 16:37
  • Are you sure you don't have the `User` properties assigned to empty strings? – Kos Dec 22 '16 at 16:45

4 Answers4

36

Just decorating the properties [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] ONLY should do what you want. Unless the property is getting set to an empty string.

Just wondering, why do you need the DataMemeber attribute?

Here is a link to a working dotnetfiddle

using System;
using Newtonsoft.Json;
using System.ComponentModel;

public class Program
{

    public static void Main()
    {
        var user = new User();

        user.UserID = "1234";
        user.ssn = "";

        var settings = new JsonSerializerSettings();

        settings.NullValueHandling = NullValueHandling.Ignore;
        settings.DefaultValueHandling = DefaultValueHandling.Ignore;


        Console.WriteLine(JsonConvert.SerializeObject(user, settings));
    }
}

public class User
{
    [DefaultValue("")]
    public string UserID { get; set; }

    [DefaultValue("")]
    public string ssn { get; set; }

    [DefaultValue("")]
    public string empID { get; set; }

    [DefaultValue("")]
    public string schemaAgencyName { get; set; }

    [DefaultValue("")]
    public string givenName { get; set; }

    [DefaultValue("")]
    public string familyName { get; set; }

    [DefaultValue("")]
    public string password { get; set; }
}
Brenton Major
  • 500
  • 6
  • 10
  • In your sample on dotnetfiddle I see ssn coming back as well. You set only userID to a value and ssn as "". I want only UserID. :(. Datamember because I have to use EmitDefaultValue and IsRequired attributes. – RookieAppler Dec 22 '16 at 16:46
  • Updated Fiddle to not serialize empty strings by using the DefaultValue Attribute and default value handling in settings. Updating answer to include code snippet. – Brenton Major Dec 22 '16 at 16:59
  • It also Ignore zero values for int variables, false values for boolean variables and all other default values. Is there any way that just Ignore string empty? – ali-myousefi Dec 09 '19 at 09:07
  • 3
    @ali-myousefi You can remove the `settings.DefaultValueHandling = DefaultValueHandling.Ignore;` line and probably remove the DefaultValue attributes. The default for string is NULL so `settings.NullValueHandling = NullValueHandling.Ignore;` should be all you need. .NET Fiddle for refrence https://dotnetfiddle.net/sVC0UU – Brenton Major Dec 11 '19 at 00:22
  • Any idea how to also do not include properties with only whitespace? I tried reg expression but that did not work. – Adam Bielecki Dec 12 '19 at 09:23
16

You can also use two annotations as follows:

[DefaultValue("")]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public string Category { get; set; }
Burak Doğan
  • 161
  • 1
  • 4
9

Although the accepted answers works, it also removes integer properties of zero value. I was looking for something more generic to work with large objects.

Found a great answer here: https://codearticles.ru/articles/2905?AspxAutoDetectCookieSupport=1

And consolidated it for our use case as below:

public class ShouldSerializeContractResolver : DefaultContractResolver
{
    public static readonly ShouldSerializeContractResolver Instance = new ShouldSerializeContractResolver();

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty property = base.CreateProperty(member, memberSerialization);

        if (property.PropertyType == typeof(string))
        {
            // Do not include emptry strings
            property.ShouldSerialize = instance =>
            {
                return !string.IsNullOrWhiteSpace(instance.GetType().GetProperty(member.Name).GetValue(instance, null) as string);
            };
        }
        else if (property.PropertyType == typeof(DateTime))
        {
            // Do not include zero DateTime
            property.ShouldSerialize = instance =>
            {
                return Convert.ToDateTime(instance.GetType().GetProperty(member.Name).GetValue(instance, null)) != default(DateTime);
            };
        }
        else if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
        {
            // Do not include zero-length lists
            switch (member.MemberType)
            {
                case MemberTypes.Property:
                    property.ShouldSerialize = instance =>
                    {
                        var enumerable = instance.GetType().GetProperty(member.Name).GetValue(instance, null) as IEnumerable;
                        return enumerable != null ? enumerable.GetEnumerator().MoveNext() : false;
                    };
                    break;
                case MemberTypes.Field:
                    property.ShouldSerialize = instance =>
                    {
                        var enumerable = instance.GetType().GetField(member.Name).GetValue(instance) as IEnumerable;
                        return enumerable != null ? enumerable.GetEnumerator().MoveNext() : false;
                    };
                    break;
            }
        }
        return property;
    }
}

This can be used as follows:

JsonConvert.SerializeObject(o,
    Newtonsoft.Json.Formatting.None,
    new JsonSerializerSettings
    {
        NullValueHandling = NullValueHandling.Ignore,
        ContractResolver = ShouldSerializeContractResolver.Instance
    });
Nick Evans
  • 3,279
  • 2
  • 25
  • 21
3

i have done this with a converter.

using System;

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace TestApp.JsonConverterResolver
{
    public class IgnoreEmptyStringsConverter : JsonConverter
    {
    #region Methods

        public override bool CanConvert(Type objectType)
        {
            return objectType == typeof(string);
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
                                        JsonSerializer serializer)
        {
            var theValue = reader.Value?.ToString();

            return !string.IsNullOrWhiteSpace(theValue) ? theValue : null;
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            if (!string.IsNullOrWhiteSpace(value.ToString()))
            {
                JToken token = JToken.FromObject(value.ToString(), serializer);
                token.WriteTo(writer);
                return;
            }

            writer.WriteNull();
        }

    #endregion
    }
}

Example person model class:

public class Person
{
    public string Name { get; set; }
}

And the ueage:

var serializerSettings = new JsonSerializerSettings
     {
         Formatting           = Newtonsoft.Json.Formatting.Indented,
         NullValueHandling    = Newtonsoft.Json.NullValueHandling.Ignore,
         Converters           = new List<JsonConverter> {new IgnoreEmptyStringsConverter()}
     };

var person = JsonConvert.DeserializeObject<Person>("{ \"Name\":\"\" }", serializerSettings);

var jsonPerson = JsonConvert.SerializeObject(new Person { Name = "" }, serializerSettings);

I just wrote that out of my head. But I think that's how I solved it at some point. Maybe it helps someone.

Marcus
  • 654
  • 1
  • 8
  • 19