Home > Mobile >  Raise error when de-serialising json to an object with mismatched properties
Raise error when de-serialising json to an object with mismatched properties

Time:08-25

C# code below:

// See https://aka.ms/new-console-template for more information

using static System.Console;
using System.Text.Json;

List<Person> source = new List<Person>();

using (StreamReader r = new StreamReader("data.json"))
{
    string json = r.ReadToEnd();
    source = JsonSerializer.Deserialize<List<Person>>(json);

    foreach (Person person in source)
    {
        WriteLine(person.Id);
    }
    
}

public class Person
{    
    public int Id { get; set; }
    public string Firstname { get; set; }
    public string Lastname { get; set; }
    public string City { get; set; }
}

And the json file:

[
  {
    "Id": 1,
    "Firstname": "Hello",
    "Lastname": "World",
    "City": "New York"
  },
  {
    "Id": 2,
    "Firstname": "Foo",
    "Lastname": "Bar",
    "City": "Phoenix"
  }
]

I wish to raise and catch an error when there is mismatch between the properties of the person in the json file and the Person class in the C# code - is this possible?

CodePudding user response:

You can handle any property that doesn't match with a corresponding one in your specified objects.

This is called JSON overflow (all other properties in the JSON will be stored in a Dictionary), and it works using the JsonExtensionData attribute.

Just declare another property in your object:

public class Person
{    
    public int Id { get; set; }
    public string Firstname { get; set; }
    public string Lastname { get; set; }
    public string City { get; set; }
    [JsonExtensionData]
    public Dictionary<string, JsonElement>? OtherData { get; set; }
}

This way, if you get a JSON with extra properties or properties with a typo in its name, like:

  {
    "Id": 1,
    "Firstname": "Hello",
    "lastNam": "World",
    "City": "New York",
    "Age": "32"
  }

You'll get your deserialized object within the OtherProperties dictionary, indexed by property name. In this case, you'll get two items in the dictionary: Age and lastNam with their respective values as JsonElements.

This way you could just check if there are elements in any objects dictionary, and throw an error. In your example and using linq:

if(source.Any(p => p.OtherData != null && p.OtherData.Any())) {
   throw new Exception("JSON contains invalid or unexpected properties");
}

More details in this article: https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-handle-overflow?pivots=dotnet-6-0

CodePudding user response:

Additionally to the post-deserialization option, to properly validate JSON by defining properties that must be there (required) and that must have a specific type and values, I recommend you using a JSON Schema to pre-validate the JSON input before deserializing it.

This way you can declare a schema like:

{
   type: "array",
   minItems: 1,
   items: {
      type: "object",
      properties: {
         Id: { type: "number" },
         Firstname: { type: "string" },
         Lastname: { type: "string" },
         City: { type: "string" }
      },
      required: [ "Id", "Firstname", "Lastname", "City" ],
      additionalProperties: false
   }
}

This schema above for example, defines that the JSON must be an array of objects with at least one item, and such items must have all four properties: Id of type number and Fistname, Lastname, and City of type string, with no additional properties.

Here is an article that talks about how to implement it in C#: https://endjin.com/blog/2021/05/csharp-serialization-with-system-text-json-schema

  • Related