8

I'm having trouble finding a way to automatically deserialize (server side) all EmptyOrWhiteSpace strings to null . Json.Net by default simply assigns the value to the object property, and I need to verify string by string whether it is empty or white space, and then set it to null.

I need this to be done upon deserialization, so I don't have to remember to verify every single string that comes from the client.

How can I override this on Json Net?

10
  • 1
    newtonsoft.com/json/help/html/… Commented Oct 4, 2016 at 15:05
  • 3
    @garfbradaz how does that solve his problem? That setting tells it how to handle null, not how to handle blank strings. Commented Oct 4, 2016 at 15:06
  • 2
    @garfbradaz I don't have null values that need to be handled, I have empty strings that need to be converted to null. Commented Oct 4, 2016 at 15:07
  • 2
    stackoverflow.com/a/23832417/961526 : you just need to invert the logic ? Commented Oct 4, 2016 at 15:11
  • 2
    Not sure why this is closed as unfocused? It's askign a clear single question - How do I configured Newtonsoft to deserialise EmptyString to Null? Commented Jun 4, 2020 at 11:49

1 Answer 1

6

After a lot of source digging, I solved my problem. Turns out all the solutions proposed in the comments only work if I am deserializing a complex object which contains a property that is a string. In this case, yes, simply modifying the contract resolver works [1].

However, what I needed was a way to convert any string to null upon deserialization, and modifying the contract this way will fail for the case where my object is just a string, i.e.,

public void MyMethod(string jsonSomeInfo)
{
  // At this point, jsonSomeInfo is "\"\"",
  // an emmpty string.

  var deserialized = new JsonSerializer().Deserialize(new StringReader(jsonSomeInfo), typeof(string));

  // deserialized = "", event if I used the modified contract resolver [1].
}

What happens is that when we work with a complex object, internally JSON.NET assigns a TokenType of JsonToken.StartObject to the reader, which will cause the deserialization to follow a certain path where property.ValueProvider.SetValue(target, value); is called.

However, if the object is just a string, the TokenType will be JsonToken.String, and the path will be different, and the value provider will never be invoked.

In any event, my solution was to build a custom converter to convert JsonReaders that have TokenType == JsonToken.String (code below).

Solution

public class StringConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(string);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
            if (reader.Value == null) return null;

            string text = reader.Value.ToString();

            if (string.IsNullOrWhiteSpace(text))
            {
                return null;
            }

            return text;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException("Not needed because this converter cannot write json");
    }

    public override bool CanWrite
    {
        get { return false; }
    }
}

[1] Credits to @Raphaël Althaus.

public class NullToEmptyStringResolver : DefaultContractResolver
{
    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        return type.GetProperties()
        .Select(p => {
            var jp = base.CreateProperty(p, memberSerialization);
            jp.ValueProvider = new EmptyToNullStringValueProvider(p);
            return jp;
        }).ToList();
    }
}

public class EmptyToNullStringValueProvider : IValueProvider
{
    PropertyInfo _MemberInfo;

    public EmptyToNullStringValueProvider(PropertyInfo memberInfo)
    {
        _MemberInfo = memberInfo;
    }

    public object GetValue(object target)
    {
        object result = _MemberInfo.GetValue(target);

        if (_MemberInfo.PropertyType == typeof(string) && result != null && string.IsNullOrWhiteSpace(result.ToString()))
        {
            result = null;
        }

        return result;
    }

    public void SetValue(object target, object value)
    {
        if (_MemberInfo.PropertyType == typeof(string) && value != null && string.IsNullOrWhiteSpace(value.ToString()))
        {
            value = null;
        }

        _MemberInfo.SetValue(target, value);
    }
}
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.