25

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.

2
  • 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? Commented Dec 22, 2016 at 16:37
  • Are you sure you don't have the User properties assigned to empty strings? Commented Dec 22, 2016 at 16:45

5 Answers 5

39

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; }
}
Sign up to request clarification or add additional context in comments.

5 Comments

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.
Updated Fiddle to not serialize empty strings by using the DefaultValue Attribute and default value handling in settings. Updating answer to include code snippet.
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 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 dotnetfiddle.net/sVC0UU
Any idea how to also do not include properties with only whitespace? I tried reg expression but that did not work.
18

You can also use two annotations as follows:

[DefaultValue("")]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public string Category { get; set; }

2 Comments

Nice and clean solution
DefaultValue comes from System.ComponentModel namespace
14

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

1 Comment

best approach so far - no need to add attributes to generated classes
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.

Comments

0

It's soooo simple

just put the ShouldSerializePPPPP() in the class You can then define your own rule

public string Contact { get; set; } = string.Empty;
public bool ShouldSerializeContact() => !string.IsNullOrWhiteSpace(Contact);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.