Home > Enterprise >  How to remove empty string properties from json using System.Text.Json.JsonSerializer?
How to remove empty string properties from json using System.Text.Json.JsonSerializer?

Time:01-14

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!

CodePudding user response:

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

CodePudding user response:

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"
}
  • Related