skip to Main Content

I want to omit empty string properties from my Json.

Here’s what I did.

Create a Custom Converter that converts empty strings to null:

public class EmptyStringToNullConverter : JsonConverter<string>
{
    public override string? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        return reader.GetString();
    }

    public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options)
    {
        if (value == string.Empty)
            writer.WriteNullValue();
        else
            writer.WriteStringValue(value);
    }
}
    

I have a JsonSerializerClass, which basically is a wrapper around System.Text.Json.JsonSerializer and this class is using the Converter:

    public class JsonSerializerClass
{
    private JsonSerializerOptions serializeOptions;
    private JsonSerializerOptions deserializeOptions;

    public JsonSerializerClass()
    {
        serializeOptions = new JsonSerializerOptions();
        deserializeOptions = new JsonSerializerOptions();
        InitSerializer();
        InitDeserializer();
    }

    public JsonSerializerOptions SerializeOptions => serializeOptions;

    public string Serialize(object value)
    {
        return Serialize(value, serializeOptions);
    }

    public T Deserialize<T>(string json)
    {
        var o = System.Text.Json.JsonSerializer.Deserialize<T>(json, deserializeOptions);

        if (o == null)
            throw new ArgumentException($"Cannot deserialize JSON to type {typeof(T)}: {json}");

        return o;
    }

    public static string Serialize(object value, JsonSerializerOptions options)
    {
        return System.Text.Json.JsonSerializer.Serialize(value, options);
    }

    private void InitSerializer()
    {
        serializeOptions.Converters.Add(new EmptyStringToNullConverter());

        serializeOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
    }

    private void InitDeserializer()
    {
        deserializeOptions.PropertyNameCaseInsensitive = true;

        deserializeOptions.Converters.Add(new JsonStringEnumConverter());
        deserializeOptions.Converters.Add(new DateOnlyConverter());
        deserializeOptions.Converters.Add(new DateOnlyNullableConverter());
        deserializeOptions.Converters.Add(new IntConverter());
        deserializeOptions.Converters.Add(new DecimalConverter());

        deserializeOptions.IncludeFields = true;
        deserializeOptions.AllowTrailingCommas = true;
        deserializeOptions.NumberHandling = JsonNumberHandling.AllowReadingFromString;
        deserializeOptions.ReadCommentHandling = JsonCommentHandling.Skip;
    }
}    

And here’s my test case:

var person = new Person
{
    Name = "John",
    LastName = "Doe",
    MiddleName = ""
};
var serializer = new JsonSerializerClass();

var json = serializer.Serialize(person);

json.ToLower().Should().NotContain(nameof(person.MiddleName).ToLower());

I would expect that MiddleName is not in the json because empty string becomes null and null values should be removed from the json. But instead MiddleName is in the json, with value null!

2

Answers


  1. In .NET 7, you can use a Modifier to influence the serialization process (more information in the announcement blog):

    static void ExcludeEmptyStrings(JsonTypeInfo jsonTypeInfo)
    {
        if (jsonTypeInfo.Kind != JsonTypeInfoKind.Object)
            return;
    
        foreach (JsonPropertyInfo jsonPropertyInfo in jsonTypeInfo.Properties)
        {
            if (jsonPropertyInfo.PropertyType == typeof(string))
            {
                jsonPropertyInfo.ShouldSerialize = static (obj, value) => 
                    !string.IsNullOrEmpty((string)value);
            }
        }
    }
    

    In this example, it’s all about the ShouldSerialize property, which takes in the value and allows you to decide whether or not it should be serialized.

    Add the modifier using the property I linked above:

    var jsonSerializerOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web)
    {
        TypeInfoResolver = new DefaultJsonTypeInfoResolver
        {
            Modifiers = { ExcludeEmptyStrings }
        }
    };
    

    .NET Fiddle

    Login or Signup to reply.
  2. you can try this custom converter

        var options = new System.Text.Json.JsonSerializerOptions { WriteIndented = true };
        options.Converters.Add(new IgnoreNullOrEmptyStringJsonConverter<Person>());
        var json = System.Text.Json.JsonSerializer.Serialize(person, options);
    
    public class IgnoreNullOrEmptyStringJsonConverter<T> : System.Text.Json.Serialization.JsonConverter<T> where T : class
    {
        public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            // Use default implementation when deserializing (reading)
            return System.Text.Json.JsonSerializer.Deserialize<T>(ref reader, options);
        }
    
        public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
        {
            writer.WriteStartObject();
    
            using (JsonDocument document = System.Text.Json.JsonSerializer.SerializeToDocument(value))
            {
                foreach (var property in document.RootElement.EnumerateObject())
                {
                    if (property.Value.ValueKind.ToString() != "Null" 
                       && !(property.Value.ValueKind.ToString() == "String" 
                       && string.IsNullOrEmpty(property.Value.ToString())))
                        property.WriteTo(writer);
                }
            }
            writer.WriteEndObject();
        }
    }
    

    output

    {
      "Name": "John",
      "LastName": "Doe"
    }
    

    or you can use this function

        var json = GetIgnoreNullOrEmptyStringJson(person);
    
    
    public string GetIgnoreNullOrEmptyStringJson<T>(T value) where T : class
    {
        var jNode = System.Text.Json.JsonSerializer.SerializeToNode(value);
    
        var props = jNode.AsObject()
            .Where(prop => (prop.Value == null
                   || (prop.Value.GetValue<JsonElement>().ValueKind.ToString() == "String"
                   && string.IsNullOrEmpty(prop.Value.ToString()))))
            .Select(e => e)
            .ToArray();
    
        for (var i = 0; i < props.Length; i++)
            jNode.AsObject().Remove(props[i].Key);
    
        return System.Text.Json.JsonSerializer.Serialize(jNode, new System.Text.Json.JsonSerializerOptions { WriteIndented = true });
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search