Search code examples
c#.netjsonjavascriptserializer

JavaScriptSerializer.Deserialize() skip property if cannot parse it


I am deserializing JSON strings into using Person objects using this code:

JavaScriptSerializer serializer = new JavaScriptSerializer();
Person person = serializer.Deserialize<Person>(jsonString);

The Person class has an Age property:

int Age {get;set;}

The JSON string has a value like:

{age: 'not valid int'}

and I am getting an exception as follows:

Cannot cast string to Int32

Is there any way to tell the JavaScriptSerializer to skip on error and continue with the other properties?


Solution

  • Yes, it is possible to control the deserialization process by writing a custom JavaScriptConverter class:

    public class PersonConverter : JavaScriptConverter
    {
        public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
        {
            Person person = new Person();
    
            foreach (string key in dictionary.Keys)
            {
                var value = dictionary[key];
    
                switch (key)
                {
                    case "Name":
                        person.Name = (string)value;
                        break;
    
                    case "Age":
                        {
                            if (value is int)
                            {
                                person.Age = (int)value;
                            }
                            else
                            {
                                int age;
                                if (int.TryParse((string)dictionary[key], out age))
                                {
                                    person.Age = age;
                                } // else leave Age as null (or if int, leave as 0); alternatively put an else block here to set to value of your choice
                            }
                        }
                        break;
                }
            }
    
            return person;
        }
    
        public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
        {
            throw new NotImplementedException();
        }
    
        public override IEnumerable<Type> SupportedTypes
        {
            get
            {
                return new[] { typeof(Person) };
            }
        }
    }
    

    I have taken the liberty of making Person.Age nullable, where null indicates that the age is unknown, but if this is not acceptable you could modify the converter to default to 0 or -1 when the age was not parseable:

    public class Person
    {
        public string Name { get; set; }
        public int? Age { get; set; }
    }
    

    Usage:

    var serializer = new JavaScriptSerializer();
    serializer.RegisterConverters(new[] { new PersonConverter() });
    var person = serializer.Deserialize<Person>(jsonString);
    

    Example with output:

        var serializer = new JavaScriptSerializer();
        serializer.RegisterConverters(new[] { new PersonConverter() });
    
        var jsonStrings = new List<string>
        {
            "{ Name: 'Steve',  Age: 21 }",
            "{ Name: 'Teoman',  Age: 'not valid int'}",
            "{ Name: 'Queen Elizabeth II',  Age: '89'}"
        };
    
        foreach (var jsonString in jsonStrings)
        {
            var person = serializer.Deserialize<Person>(jsonString);
            Console.WriteLine("Name: {0}, Age: {1}", person.Name, person.Age);
        }
    

    Name: Steve, Age: 21

    Name: Teoman, Age:

    Name: Queen Elizabeth II, Age: 89

    Please note that we have handled here: a literal number, a number inside a string, and a totally invalid value.