skip to Main Content

Below is the default code to parse a JsonArray, which works totally fine if there is of course no error or typo in the Json string:

public class Person
{
    public int Id { get; set; }
    public required string Name { get; set; }
}

Program code:

var jsonString = @"
[ 
    { ""Id"": 1, ""Name"": ""name1"" },
    { ""Id"": 2, ""Name"": ""name2"" },
    { ""Id"": 3, ""NameX"": ""name3"" },
    { ""Id"": 4, ""Name"": ""name4"" },
    { ""Id"": 5, ""Name"": ""name5"" }
]
";

List<Person> people = JsonSerializer.Deserialize<List<Person>>(jsonString) ?? [];
    
Console.WriteLine(people.Count);

But notice that in the jsonString, there is unfortunately a typo. Of course, this results in a runtime error. It is possible to wrap the Deserialize<> in a try-catch, but then I have 0 people in my people array.

This is a simplification of the situation I have of course, in my case I get the json from an external source. (Assume it is not possible to skip the required attribute for string Name in this example)

How can I change this code so that I basically have a TryDeserialize<>? I would like to parse every correct object, and skip (and log if that is possible) all the objects that aren’t correct? So that my final people array has 4 people in it (ids 1,2,4,5)?

2

Answers


  1. For such task you would need to implement custom JSON converter for list of Person objects.

    Here’s example implementation:

    public class ListPfPeopleJsonConverter : JsonConverter<List<Person>>
    {
        public override List<Person>? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            var people = new List<Person>();
            reader.Read();
    
            while (reader.TokenType == JsonTokenType.StartObject)
            {
                if(TryReadPerson(ref reader, out var person))
                {
                    people.Add(person);
                }
            }
    
            return people;
        }
    
        private static bool TryReadPerson(ref Utf8JsonReader reader, out Person person)
        {
            person = null;
            try
            {
                person = JsonSerializer.Deserialize<Person>(ref reader);
                return true;
            }
            catch
            {
                ConsumeJsonObject(ref reader);
                return false;
            }
            finally
            {
                // Move to next item.
                reader.Read();
            }
        }
    
        private static void ConsumeJsonObject(ref Utf8JsonReader reader)
        {
            // Consume invalid item to the "end of object"
            while (reader.TokenType != JsonTokenType.EndObject)
            {
                reader.Read();
            }
        }
    
        public override void Write(Utf8JsonWriter writer, List<Person> value, JsonSerializerOptions options)
        {
            throw new NotImplementedException();
        }
    }
    

    And then the usage:

    var jsonString = @"
        [ 
            { ""Id"": 1, ""Name"": ""name1"" },
            { ""Id"": 2, ""Name"": ""name2"" },
            { ""Id"": 3, ""NameX"": ""name3"" },
            { ""Id"": 4, ""Name"": ""name4"" },
            { ""Id"": 5, ""Name"": ""name5"" }
        ]
        ";
    
    var options = new JsonSerializerOptions();
    options.Converters.Add(new ListPfPeopleJsonConverter());
    
    List<Person> people = JsonSerializer.Deserialize<List<Person>>(jsonString, options) ?? [];
    
    Console.WriteLine(people.Count);
    
    Login or Signup to reply.
  2. One option would be to use a JsonReader instead.

    static void Main(string[] args)
    {
        var jsonString = @"
        [ 
            { ""Id"": 1, ""Name"": ""name1"" },
            { ""Id"": 2, ""Name"": ""name2"" },
            { ""Id"": 3, ""NameX"": ""name3"" },
            { ""Id"": 4, ""Name"": ""name4"" },
            { ""Id"": 5, ""Name"": ""name5"" },
            { ""IdX"": 1, ""Name"": ""name6"" }
        ]
    ";
    
        List<Person> people = new List<Person>();
        var reader = new Utf8JsonReader(System.Text.Encoding.UTF8.GetBytes(jsonString));
    
        while (reader.Read())
        {
            if (reader.TokenType == JsonTokenType.StartObject)
            {
                int? id = null;
                string? name = null;
    
                while (reader.Read() && reader.TokenType != JsonTokenType.EndObject)
                {
                    if (reader.TokenType == JsonTokenType.PropertyName)
                    {
                        string? propertyName = reader.GetString();
                        reader.Read();
    
                        if (propertyName == "Id")
                            id = reader.GetInt32();
                        else if (propertyName == "Name")
                            name = reader.GetString();
                    }
                }
    
                if (name != null && id != null)
                    people.Add(new Person { Id = id.Value, Name = name });
            }
        }
    
        Console.WriteLine(people.Count);
    }
    
    public class Person
    {
        public int Id { get; set; }
        public required string Name { get; set; }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search