1

I'm trying to deserialize a JSON file but I'm currently stuck because of the Array "statistics".

My JSON file looks like this:

[
  {
    "name": "itemName",
    "level": 8,
    "imgUrl": "https://imgItemName.png",
    "description": "itemDescription.",
    "statistics": [
    {
        "Vitality": {
          "min": 10,
          "max": 13
        }
    }
    ]
  }
]

My classes look like this:

public class Item
{
  public string name;
  public int level;
  public string type;
  public string imgUrl;
  public string description;
  public List<Statistics>statistics;
  //public List<JArray> statistics;
  //public Statistics[] statistics;
  
}

public class Statistics
{
  public string Vitality;
  public List<MinMax> minmax;
  //public MinMax[] minMax;
}

public class MinMax
{
  public string min;
  public string max;
}

And this is how I'm trying to deseralize the file:

string json = System.IO.File.ReadAllText("items.json");
var listItems = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Item>>(json);

I have already tried many combinations but cannot recover the Statistics key in any case. Any help will be really appreciated :)

Yong Shun
  • 35,286
  • 4
  • 24
  • 46
Exarilo
  • 41
  • 2
  • 2
    Do not name classes in the plural, name properties that return collections in the plural eg `List Statistics` – Caius Jard Dec 27 '21 at 08:53
  • 1
    A vitality contains no [ ] and is hence not a list of minmax. A statistics is a list of statistic, and a statistic has a vitality property that is an object of min and max. If you want classes that work, paste your json into http://QuickType.io (there isn't much point puzzling classes out of json yourself; computers can do it much faster and more comprehensively) – Caius Jard Dec 27 '21 at 08:54
  • 1
    @CaiusJard I'm more partial to https://json2csharp.com/ - but only because I'm a contributor there ([I implemented their support for immutable DTOs](https://github.com/Json2CSharp/Json2CSharpCodeGenerator/pull/42) :) ) – Dai Dec 27 '21 at 08:59
  • your class didn't represent the JSON, try some of this: https://stackoverflow.com/questions/21611674/how-to-auto-generate-a-c-sharp-class-file-from-a-json-string – J.Salas Dec 27 '21 at 09:01
  • Thanks for the answers I don't know about these sites but it will help me a lot ! – Exarilo Dec 27 '21 at 09:02
  • Yeah, it's starting to catch up to QT.. But there's still a way to go, and J2CS's ads are massively more obtrusive than QT's.. – Caius Jard Dec 27 '21 at 09:06
  • @CaiusJard That's why I contributed the WinForms GUI as an alternative :) – Dai Dec 27 '21 at 09:07

2 Answers2

3

With the tool Json2Csharp, your model should be as below:

public class Vitality
{
    [JsonProperty("min")]
    public int Min { get; set; }

    [JsonProperty("max")]
    public int Max { get; set; }
}

public class Statistic
{
    [JsonProperty("Vitality")]
    public Vitality Vitality { get; set; }
}

public class Root
{
    [JsonProperty("name")]
    public string Name { get; set; }

    [JsonProperty("level")]
    public int Level { get; set; }

    [JsonProperty("imgUrl")]
    public string ImgUrl { get; set; }

    [JsonProperty("description")]
    public string Description { get; set; }

    [JsonProperty("statistics")]
    public List<Statistic> Statistics { get; set; }
}

Sample Demo on .NET Fiddle

Yong Shun
  • 35,286
  • 4
  • 24
  • 46
  • 1
    `public string name { get; set; }` <-- Ew. – Dai Dec 27 '21 at 09:00
  • 1
    Please.. turn on the option that puts [JsonProperty] attributes in so we don't end up with a load of JavaScript flavored C# – Caius Jard Dec 27 '21 at 09:01
  • Ok, ya, naming convention should be started with Pascal case, and we can use JsonProperty attribute to control the name. Thanks for the advise, I correct my answer now =) – Yong Shun Dec 27 '21 at 09:01
  • @YongShun Actually my concern was more about how there's _zero guarantees_ about the state of those `string` properties: `string` is a reference-type and all of those properties will be `null` by default, but the class as-is implies everything is _somehow_ non-null when there's no ctor to guarantee that. – Dai Dec 27 '21 at 09:03
  • you should avoid using the attrbuite [JsonProperty], instead configure your serializer to be case insensitive. [JsonProperty] is bound to a single Serializer, e.g., Newtonsoft, and if you ever need to change the serializer to Json.Text or any other, those attributes have no use any longer. – Mihail Dec 27 '21 at 09:20
  • @MihailBrinza I'm going to disagree with you there, because depending on serializer settings introduces _non-local effects_ which is a common source of bugs, especially with DTOs. Also, both `System.Text.Json` and `Newtonsoft.Json` use the same attribute names, so the DTO definitions are _largely_ source-compatible. – Dai Dec 27 '21 at 09:35
  • @Dai I might be wrong, but NewtonSoft uses [JsonProperty], Json.Text uses [JsonPropertyName]. And there is no real reason to use then unless you want to serialize them to a different name, e.g., a property named "Username" to "DisplayName". Json.Text Docs: https://docs.microsoft.com/en-us/dotnet/api/system.text.json.serialization.jsonpropertynameattribute?view=net-6.0 NewtonsoftDocs: https://www.newtonsoft.com/json/help/html/jsonpropertyname.htm from .net core 2.1 to 3.1 the serializer was changed, using attributes it would've stopped working, or you would have tons of unused attributes. – Mihail Dec 28 '21 at 10:08
  • 1
    @MihailBrinza You are correct about the attribute name. I was thinking of my own code where I added a `using JsonProperty = System.Text.Json.JsonPropertyName` alias to simplify transition. – Dai Dec 28 '21 at 10:09
  • exactly, a cleaner way to do it is to define a JsonNamingPolicy that works across all DTOs, without needing to add attributes to each property, one by one. – Mihail Dec 28 '21 at 10:11
  • @MihailBrinza However I disagree with you about using `[JsonProperty]` (or `[JsonPropertyName]` for _only_ setting a different name. I like to write software that's self-describing and also _resistant to unintentional breakages_. For example, if you have a DTO `class` that implements some unrelated-to-DTOs `interface` (e.g. `class PurchaseOrderDto` and `interface IHasPurchaseOrderDetails` is a domain interface) and you do a VS _Refactor Rename_ on member-properties of the interface then you would unintentionally break the DTO, but if it had `[JsonProperty]` attributes then it would still work. – Dai Dec 28 '21 at 10:12
0

If you are using Visual studio you can generate the C# classes bazed on Json by going to Edit-> paste special -> Paste JSON as Classes after that add the Fix the C# name convention and you should be good to go

    public class Item
    {
        [JsonProperty("name")]
        public string Name { get; set; }
        [JsonProperty("level")]
        public int Level { get; set; }
        [JsonProperty("imgUrl")]
        public string ImgUrl { get; set; }
        [JsonProperty("description")]
        public string Description { get; set; }
        [JsonProperty("statistics")]
        public Statistic[] Statistics { get; set; }
    }
    
    public class Statistic
    {
        public Vitality Vitality { get; set; }
    }
    
    public class Vitality
    {
        [JsonProperty("min")]
        public int Min { get; set; }
        [JsonProperty("max")]
        public int Max { get; set; }
    }

Game dev
  • 296
  • 2
  • 17