Remove empty string properties from json serialized object
Asked Answered
C

5

23

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.

Coffeepot answered 22/12, 2016 at 16:3 Comment(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?Pincer
Are you sure you don't have the User properties assigned to empty strings?Pincer
D
38

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; }
}
Dutiful answered 22/12, 2016 at 16:38 Comment(5)
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.Coffeepot
Updated Fiddle to not serialize empty strings by using the DefaultValue Attribute and default value handling in settings. Updating answer to include code snippet.Dutiful
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?Albritton
@Albritton 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/sVC0UUDutiful
Any idea how to also do not include properties with only whitespace? I tried reg expression but that did not work.Wacker
M
16

You can also use two annotations as follows:

[DefaultValue("")]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public string Category { get; set; }
Mender answered 8/4, 2020 at 17:36 Comment(2)
Nice and clean solutionPaunchy
DefaultValue comes from System.ComponentModel namespaceH
S
10

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
    });
Spadix answered 21/12, 2018 at 1:25 Comment(0)
N
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.

Noel answered 31/7, 2020 at 0:44 Comment(0)
R
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);
Rossen answered 20/7 at 8:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.