6

I have the following class definitions:

public class Tag
{
    public Guid? TagId { get; set; }
    public string TagText { get; set; }
    public DateTime CreatedOn { get; set; }
}

public class Wiki
{
    public Guid? WikiId { get; set; }
    public string WikiText { get; set; }
    public string Title { get; set; }
    public DateTime CreatedOn { get; set; }
    public IEnumerable<Tag> Tags { get; set; }
}

From the database i get the following json Object:

{
    "WikiId": "83981284-0AD3-4420-90AB-15E3BF6BD7B7",
    "WikiText": "Text",
    "Title": "Title",
    "CreatedOn": "2017-08-07T09:16:06.0800000",
    "Tags": [{}] // <-- here i would like to ignore the empty Tag object
}

When i do now a JsonConvert.DeserializeObject<Wiki>(json) i get a Wiki object with a list of 1 Tag with the values TagId: null, TagText: null and CreatedOn: "0001-01-01T00:00:00"

Is there a way to ignore the empty Tag object while deserializing? I have tried several JsonSerializerSettings but nothing helped.

8
  • I don't understand your question... Do you want to ignore the whole object, or only the "Tags" property? Commented Aug 8, 2017 at 16:04
  • If you want only to ignore the "Tags" property, then you have a problem, because your class "Wiki" contains the property "Tags". If you really want to ignore that property, you have to declare two "Wiki" classes: One with "Tags" property and one without the "Tags" property. And then you have to check if the JSON string contains a string like "[{}]". If it contains "[{}]", then the "Tags" is empty and you can use the class without the "Tags" property for deserializing.. If you want to remove "Tags" always, then you can remove the "Tags" property in your "Wiki" class and it should work. Commented Aug 8, 2017 at 16:12
  • is there any chance you can preprocess the string before parsing? like: jsonStringFromDb.Replace("{}", "") Commented Aug 8, 2017 at 16:23
  • @SeanStayn i want to ignore the whole tag object. Not the Tags property in Wiki. Commented Aug 8, 2017 at 16:40
  • @Artem yes i can preprocess the string, but i thought that there is maybe another solution with Json.Net. Commented Aug 8, 2017 at 16:40

3 Answers 3

5

You could use a custom JsonConverter to ignore the empty objects during deserialization. Something like this could work:

class IgnoreEmptyItemsConverter<T> : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType.IsAssignableFrom(typeof(List<T>));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        List<T> list = new List<T>();
        JArray array = JArray.Load(reader);
        foreach (JObject obj in array.Children<JObject>())
        {
            if (obj.HasValues)
            {
                list.Add(obj.ToObject<T>(serializer));
            }
        }
        return list;
    }

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

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

To use the converter, just add a [JsonConverter] attribute to your Tags property like this:

public class Wiki
{
    ...
    [JsonConverter(typeof(IgnoreEmptyItemsConverter<Tag>))]
    public IEnumerable<Tag> Tags { get; set; }
}

Fiddle: https://dotnetfiddle.net/hrAFsh

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the solution @Brian, helped me a lot. If someone needs a non-generic, applicable to all collections converter, have a look at the answer below.
2

You'll have to detect the empty tag objects post-conversion and remove them yourself. From the deserializer's perspective, {} is a perfectly valid and complete Tag object whose properties are all unset.

Something like the following should work (presuming C# 6):

Wiki wiki = JsonConvert.DeserializeObject<Wiki>(json);
wiki.Tags = Wiki.Tags?.Where(x => x.TagId.HasValue)?.ToList();

Comments

1

Thanks to @Brian-Rogers's brilliant answer, I was able to come up with a non-generic solution which will work on all collections instead of List only:

public class IgnoreEmptyArrayItemsConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        bool result = typeof(System.Collections.IEnumerable).IsAssignableFrom(objectType);
        return result;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var tokenIndexesToRemove = new List<int>();

        var array = JArray.Load(reader);

        for (int i = 0; i < array.Count; i++)
        {
            var obj = array[i];
            if (!obj.HasValues)
                tokenIndexesToRemove.Add(i);
        }

        foreach (int index in tokenIndexesToRemove)
            array.RemoveAt(index);

        var result = array.ToObject(objectType, serializer);
        return result;
    }

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

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

Instead of looping through the objects, deserializing them and adding them to a hardcoded List collection one by one, this solution will just remove the faulting tokens from the JArray and let the library deserialize the whole array to the type it should be.

Usage:

public class Wiki
{
    ...
    [JsonConverter(typeof(IgnoreEmptyItemsConverter))] // No generic parameter needed
    public HashSet<Tag> Tags { get; set; }
}

Fiddle: https://dotnetfiddle.net/9FCcpD

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.