Search code examples
c#jsondeserializationsystem.text.json

Why is deserializing this JSON resulting in `null`?


I have the following JSON:

{
    "countries": [
        {
            "name": "Afghanistan",
            "alternative_names": [],
            "formal_name": "Islamic Republic of Afghanistan",
            "the_prefix": false,
            "in_un": true
        }
    ]
}

The following classes:

[Serializable]
    public class CountriesData
    {
        public List<CountryData> countries;
    }

[Serializable]
    public class CountryData
    {
        /// <summary>
        /// The informal name of the country.
        /// <example>
        /// E.g. China
        /// </example>
        /// </summary>
        public string name;

        /// <summary>
        /// A <c>List<string></c> containing other informal names for the country.
        /// <example>
        /// E.g. for Myanmar, an alternative name is Burma.
        /// </example>
        /// </summary>

        public List<string> alternative_names;
        /// <summary>
        /// The formal name of the country.
        /// <example>
        /// E.g. People's Republic of China
        /// </example>
        /// </summary>
        public string formal_name;

        /// <summary>
        /// Should the word "the" come before this country's informal name?
        /// <example>
        /// E.g. for China, Spain, France, no. For (the) Gambia, (the) United Kingdom, (the) United States of America, yes.
        /// </example>
        /// </summary>
        public bool the_prefix;

        /// <summary>
        /// Is this country a UN member state?
        /// <example>
        /// E.g. for China, Spain, France, yes. For Kosovo, no.
        /// </example>
        /// </summary>
        public bool in_un;
    }

and I am running the line of code:

CountriesData data = JsonSerializer.Deserialize<CountriesData>(jsonString);

This is resulting in null. I have tested and the string jsonString does correctly contain above JSON as a string. No matter what I have tried, this always returns null.

I am getting the warning Warning CS8601 Possible null reference assignment. on this line of code.

Why is this not deserializing properly?


Solution

  • I'll explain using the code from this Fiddle:

    Your model classes contain public fields. Fields are by default ignored in serialization/deserialization.

    ... By default, fields are ignored.

    Citation from Serialization behavior

    Instead, use properties (easiest method) as shown in the code below. You can force serialization of fields, though if you so absolutely want.

    A second factor is naming:

    The recommended naming in json and C# differ. There are different ways to mitigate that. One of those is shown in below code: you can specify a name that is used in json that is supposed to be mapped to a specific property.

    using System;
    using System.Collections.Generic;
    using System.Text.Json;
    using System.Text.Json.Serialization;
                        
    public class Program
    {
        public static void Main()
        {
            var jsonString = @"
            {
        ""countries"": [
            {
                ""name"": ""NonControversialNationName"",
                ""alternative_names"": [],
                ""formal_name"": ""NonControversial Republic of NonControversistan"",
                ""the_prefix"": false,
                ""in_un"": true
            }
        ]
    }
            ";
            
            var obj = JsonSerializer.Deserialize<CountriesData>(jsonString);
            obj.Dump();
        }
    }
    
    public class CountriesData
    {
        [JsonPropertyName("countries")]
        public List<CountryData> Countries {get; set;}
    }
    
    public class CountryData
    {
        [JsonPropertyName("name")]
        public string Name {get; set;}
        [JsonPropertyName("alternative_names")]
        public List<string> AlternativeNames {get; set;}
        [JsonPropertyName("formal_name")]
        public string FormalName {get; set;}
        [JsonPropertyName("the_prefix")]
        public bool Prefix {get; set;}
        [JsonPropertyName("in_un")]
        public bool IsUnMember {get; set;}
    }
    

    Output:

    Dumping object(CountriesData)
     Countries  : [
                 {
                 AlternativeNames  : []
                 FormalName        : NonControversial Republic of NonControversistan
                 IsUnMember        : True
                 Name              : NonControversialNationName
                 Prefix            : False
       }
    ]
    

    About that CS8601 Warning: That depends if you are in a Nullable enabled environment.

    You probably might need to use CountriesData? data = JsonSerializer.Deserialize<CountriesData>(jsonString); (mind the "?")