1

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?

Charlieface
  • 52,284
  • 6
  • 19
  • 43
Robin
  • 373
  • 1
  • 3
  • 20
  • 6
    Those are public fields. Try making them public properties. – Fildor Apr 25 '23 at 12:16
  • I can't solve your problem, but you may want an `enum` for the `is_un` field, so you can have Observers there, e.g. The Vatican. Or other "more controversial" examples as well. – Kevin Anderson Apr 25 '23 at 12:17
  • @Fildor By that, do you mean I should put {get; set;} at the end? – Robin Apr 25 '23 at 12:18
  • @KevinAnderson That's a good idea, I'll consider updating my full .json file to address that at some point. – Robin Apr 25 '23 at 12:18
  • Perhaps add the code where you try to do this (in a controller? ) hard to tell without that as that may have an issue. – Mark Schultheiss Apr 25 '23 at 12:20
  • FWIW I am not a fan of those names in your class there. – Mark Schultheiss Apr 25 '23 at 12:21
  • @MarkSchultheiss Sorry, what do you mean by in the controller? I have a class that initialises this where the line `data = JsonSerializer.Deserialize(jsonString)` is, and I have confirmed via testing that `jsonString` contains the desired string. I could also show you some of a `Program.cs` file that uses it. – Robin Apr 25 '23 at 12:24
  • 1
    See https://dotnetfiddle.net/RBK2pZ – Fildor Apr 25 '23 at 12:26
  • _"By that, do you mean I should put {get; set;} at the end?"_ - basically, yes. If I remember correctly, the default settings ignore fields during serialization/deserialization. – Fildor Apr 25 '23 at 12:29
  • @Fildor I have done this and seen your dotnetfiddle post and it works, thank you. If you want, you can write an answer explaining this and I'll accept it. – Robin Apr 25 '23 at 12:30
  • 1
    @Fildor Perhaps add that fiddle code as an answer since it seems to outline the issue. – Mark Schultheiss Apr 25 '23 at 12:30
  • I'll also take into account all of your suggestions regarding renaming/changing variables. @MarkSchultheiss what is your issue with the given names? – Robin Apr 25 '23 at 12:31
  • 2
    Consider the conventions mentioned here https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/coding-conventions – Mark Schultheiss Apr 25 '23 at 12:33
  • 1
    Also consider things like `public string name` for your example might be better as ` `public string CountryName` to avoid stuff like SQL reserved names like `NAME` Then the XML comments might be redundant redundancy of redundant comments. – Mark Schultheiss Apr 25 '23 at 15:01

1 Answers1

4

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 "?")

Fildor
  • 14,510
  • 4
  • 35
  • 67
  • 2
    Good mention of the decorators/attributes on the names! I often forget those exist when looking at just the classes and the property names. FWIW Good/Great names are hard but often reduce the need for extended XML comments and go a long way to making code more understandable. – Mark Schultheiss Apr 25 '23 at 12:44
  • 1
    @MarkSchultheiss Well, it's not my preferred course of action. I tend to just ignore JSON naming conventions ;) - if I have a say in that. But sometimes you're stuck with the names you get. Then it's quite handy to be able to still have clean property names. – Fildor Apr 25 '23 at 12:47
  • 1
    Agree; good is better but bad is good as long as you know about it. FWIW when XML gets in play this gets even more complicated... – Mark Schultheiss Apr 25 '23 at 14:59