0

I'm relatively new to working with JSON and would like to deserialize content I downloaded from https://5e.tools/. I could try to anticipate all the tags/fields in a class, but the dataset example (https://www.newtonsoft.com/json/help/html/DeserializeDataSet.htm) looked more convenient so I tried that first:

using Newtonsoft.Json;
using System;
using System.Data;
using System.IO;

namespace _5EToolsConvertor
{
    class Program
    {
        static void Main(string[] args)
        {
            string json = File.ReadAllText(@"D:\Downloads\5eTools.1.116.8\data\spells\spells-phb.json");
            DataSet dataSet = JsonConvert.DeserializeObject<DataSet>(json);
            DataTable dataTable = dataSet.Tables["spell"];
            Console.WriteLine(dataTable.Rows.Count);
            foreach (DataRow row in dataTable.Rows)
            {
                Console.WriteLine(row["name"] + " - " + row["level"]); // just to check
            }
        }
    }
}

Here is part of the first item from the JSON file:

{"spell":[{"name":"Acid Splash","source":"PHB","page":211,"srd":true,"level":0,"school":"C","time":[{"number":1,"unit":"action"}],"range":{"type":"point","distance":{"type":"feet","amount":60}},"components":{"v":true,"s":true},"duration":[{"type":"instant"}], ...

I get the error:

An unhandled exception of type 'Newtonsoft.Json.JsonSerializationException' occurred in Newtonsoft.Json.dll
Unexpected JSON token when reading DataTable: StartObject. Path 'spell[0].range', line 1, position 139.

EDIT 11/30/2020 Advice in the comments suggested moving from a dataset model (spreadsheet) because some of my JSON fields are hierarchical. I've created a class with List<Jobject> for the hierarchical fields like "Time". But, I'd rather end up with all data in vanilla c# objects like Dictionary<string, object>. I'm having trouble converting. What is the best way? This code runs on the full JSON file but prints System.Collections.Generic.List1[Newtonsoft.Json.Linq.JObject]` for every "Time" object.

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;

namespace _5EToolsConvertor
{
    public class Spell
    {
        public string Name { get; set; }
        public string Source { get; set; }
        public string Page { get; set; }
        public string SRD { get; set; }
        public List<JObject> Time { get; set; }
    }




    class Program
    {
        static void Main(string[] args)
        {
            string jsontext = File.ReadAllText(@"D:\Downloads\5eTools.1.116.8\data\spells\spells-phb.json");

            JObject json = JObject.Parse(jsontext);

            // get JSON result objects into a list
            IList<JToken> results = json["spell"].Children().ToList();

            // serialize JSON results into .NET objects
            IList<Spell> spells = new List<Spell>();
            foreach (JToken item in results)
            {
                // JToken.ToObject is a helper method that uses JsonSerializer internally
                Spell spell = item.ToObject<Spell>();
                spells.Add(spell);
            }

            foreach (Spell spell in spells)
            {
                Console.WriteLine($"Name:{spell.Name} Source:{spell.Source} Page:{spell.Page} SRD:{spell.SRD} Time:{spell.Time}");
            }

-david

davideps
  • 541
  • 3
  • 13
  • Hard to say because your JSON is malformed (truncated), but if I upload to https://jsonformatter.curiousconcept.com/ it seems as though the objects in the "spell" array have multiple levels of nested object. This doesn't deserialize well to a `DataTable` which is basically a grid of named column X value row pairs. `"range"` specifically has multiple levels of nesting which may not be handled by [`DataTableConverter`](https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Converters/DataTableConverter.cs). – dbc Nov 28 '20 at 14:48
  • You may need to choose a different data model for such hierarchical data. – dbc Nov 28 '20 at 14:48
  • Thanks @dbc . Would you recommend the approach demonstrated in this example? https://www.newtonsoft.com/json/help/html/SerializingJSONFragments.htm – davideps Nov 28 '20 at 14:52
  • Can't you just load into a [`JToken`](https://www.newtonsoft.com/json/help/html/ParsingLINQtoJSON.htm)? Or design a data model using one of the tools from [How to auto-generate a C# class file from a JSON string](https://stackoverflow.com/q/21611674/3744182)? If file size is an issue the first thing you should do is avoid loading the entire thing into a `string` and load directly from a `Stream`, see [Performance Tips: Optimize Memory Usage](https://www.newtonsoft.com/json/help/html/Performance.htm#MemoryUsage). – dbc Nov 28 '20 at 15:16
  • Ah OK https://www.newtonsoft.com/json/help/html/SerializingJSONFragments.htm does indeed suggest loading into a `JToken` (or more specifically `JObject`). That can certainly work. – dbc Nov 28 '20 at 15:18
  • OK. I'll try. Thank you. – davideps Nov 28 '20 at 15:32
  • @dbc, I edited my post to reflect my current code. How can I change List into a vanilla c# like `Dictionary` or `List>` ? – davideps Nov 30 '20 at 11:02
  • 1
    @davideps See [How do I use JSON.NET to deserialize into nested/recursive Dictionary and List?](https://stackoverflow.com/q/5546142/10263). It sounds like your question may be a duplicate of this one. – Brian Rogers Nov 30 '20 at 15:47

0 Answers0