static void Advanced_3_MixedPolymorphism()
        {
            ConsoleTitle("SECTION: Mixed Polymorphism");

            Console.WriteLine("\n  Preparing all the base data:");
            // create sample object
            PlayerAdvanced player1 = new PlayerAdvanced("Binky", 20, new Weapon("sword", 5), new Armor("robe", 1));
            // create sample object -- simple parent generic assigned with derived object
            Player player2 = new PlayerAdvanced("Frosty", 20, new Weapon("bow", 3), new Armor("leather", 3));

            player1.SetTarget(player2);
            Player         player3 = new Player("Jacques", 20);
            PlayerAdvanced player4 = new PlayerAdvanced("Cherry", 20);

            player4.SetTarget(player3);
            ((PlayerAdvanced)player2).SetTarget(player1);
            List <Player> players = new List <Player> {
                player1, player2, player3, player4
            };

            JsonSerializerSettings settings1 = new JsonSerializerSettings
            {
                ContractResolver           = new PrivateSetResolver(),
                PreserveReferencesHandling = PreserveReferencesHandling.Objects,
                TypeNameHandling           = TypeNameHandling.Auto,
                ConstructorHandling        = ConstructorHandling.AllowNonPublicDefaultConstructor
            };

            Console.WriteLine("\n  Serializing with 'TypeNameHandling = TypeNameHandling.Auto':");
            string        json1       = JsonConvert.SerializeObject(players, Formatting.Indented, settings1);
            List <object> PlayerList1 = (List <object>)JsonConvert.DeserializeObject <List <object> >(json1, settings1);

            JsonSerializerSettings settings2 = new JsonSerializerSettings
            {
                ContractResolver           = new PrivateSetResolver(),
                PreserveReferencesHandling = PreserveReferencesHandling.Objects,
                TypeNameHandling           = TypeNameHandling.Objects,
                ConstructorHandling        = ConstructorHandling.AllowNonPublicDefaultConstructor
            };

            Console.WriteLine("\n  Serializing with 'TypeNameHandling = TypeNameHandling.Objects', desrializing to generic objects:");
            string        json2       = JsonConvert.SerializeObject(players, Formatting.Indented, settings2);
            List <object> PlayerList2 = (List <object>)JsonConvert.DeserializeObject <List <object> >(json2, settings2);

            Console.WriteLine("\n  Serializing with 'TypeNameHandling = TypeNameHandling.Objects', deserializing to parent object types:");
            List <Player> PlayerList3 = (List <Player>)JsonConvert.DeserializeObject <List <Player> >(json2, settings2);

            Console.WriteLine("\n  JSON Datasets:");
            DrawJson(json1);
            DrawJson(json2);

            Player player5 = (PlayerAdvanced)PlayerList1[0];
            Player player6 = (PlayerAdvanced)PlayerList1[1];
            Player player7 = null;

            try {
                player7 = (Player)PlayerList1[2];
            } catch (InvalidCastException) {
                Console.WriteLine("Cannot cast from Json.Linq.JObject to whatevs because it didn't put a $type in its JSON for this item!");
            }
            Player player8 = (PlayerAdvanced)PlayerList1[3];

            Player player9  = (PlayerAdvanced)PlayerList2[0];
            Player player10 = (PlayerAdvanced)PlayerList2[1];
            Player player11 = (Player)PlayerList2[2];
            Player player12 = (PlayerAdvanced)PlayerList2[3];

            Player player13 = PlayerList3[0];
            Player player14 = PlayerList3[1];
            Player player15 = PlayerList3[2];
            Player player16 = PlayerList3[3];

            Console.WriteLine("\n  Source Data:");
            players.ForEach(p => Console.WriteLine($"    {p.ToString()}"));

            Console.WriteLine("\n  Expected Results:");
            AreTheseEqual(player1, ((PlayerAdvanced)player2).Target);
            AreTheseEqual(player2, player1.Target);
            AreTheseEqual(player3, player4.Target);
            Console.WriteLine("  Data Set 1 (TypeNameHandling = TypeNameHandling.Auto doesn't catch all the things):");
            AreTheseEqual(player5, ((PlayerAdvanced)player6).Target);
            AreTheseEqual(player6, ((PlayerAdvanced)player5).Target);
            AreTheseEqual(player7, ((PlayerAdvanced)player8).Target);
            Console.WriteLine("  Data Set 2 (TypeNameHandling = TypeNameHandling.Objects works):");
            AreTheseEqual(player9, ((PlayerAdvanced)player10).Target);
            AreTheseEqual(player10, ((PlayerAdvanced)player9).Target);
            AreTheseEqual(player11, ((PlayerAdvanced)player12).Target);
            Console.WriteLine("  Data Set 3 (Deserializing to a class type looks cleanest in code):");
            AreTheseEqual(player13, ((PlayerAdvanced)player14).Target);
            AreTheseEqual(player14, ((PlayerAdvanced)player13).Target);
            AreTheseEqual(player15, ((PlayerAdvanced)player16).Target);
        }
        static void Advanced_4_MixedDataset()
        {
            ConsoleTitle("SECTION: Mixed Dataset (Database)");

            Console.WriteLine("\n  Preparing all the base data...");
            // create sample object
            PlayerAdvanced player1 = new PlayerAdvanced("Binky", 20, new Weapon("sword", 5), new Armor("robe", 1));
            // create sample object -- simple parent generic assigned with derived object
            Player player2 = new PlayerAdvanced("Frosty", 20, new Weapon("bow", 3), new Armor("leather", 3));

            player1.SetTarget(player2);
            ((PlayerAdvanced)player2).SetTarget(player1);
            Player         player3 = new Player("Jacques", 20);
            PlayerAdvanced player4 = new PlayerAdvanced("Cherry", 20);

            player4.SetTarget(player3);
            Location locale1 = new Location("The River", "A wide, wet river.");

            locale1.AddPlayer(player1);
            locale1.AddPlayer(player3);
            Location locale2 = new Location("The Woods", "A woody place.");

            locale2.AddPlayer(player2);
            locale2.AddPlayer(player4);
            locale1.AddConnection(locale2);
            locale2.AddConnection(locale1);

            DataSet myData1 = new DataSet();

            myData1.Locations.Add(locale1);
            myData1.Locations.Add(locale2);
            myData1.Players.Add(player1);
            myData1.Players.Add(player2);
            myData1.Players.Add(player3);
            myData1.Players.Add(player4);

            JsonSerializerSettings settings = new JsonSerializerSettings
            {
                ContractResolver           = new PrivateSetResolver(),
                PreserveReferencesHandling = PreserveReferencesHandling.Objects,
                TypeNameHandling           = TypeNameHandling.Objects,
                ConstructorHandling        = ConstructorHandling.AllowNonPublicDefaultConstructor
            };
            string  json    = JsonConvert.SerializeObject(myData1, Formatting.Indented, settings);
            DataSet myData2 = JsonConvert.DeserializeObject <DataSet>(json, settings);

            DrawJson(json);

            Console.WriteLine("\n  DataSet Contents:");
            Console.Write(myData1.DumpInfo());
            //Console.Write(myData2.DumpInfo());
            Console.WriteLine("\n  Expected Results:");
            AreTheseEqual(myData1.Players[0], ((PlayerAdvanced)myData1.Players[1]).Target);
            AreTheseEqual(myData1.Players[1], ((PlayerAdvanced)myData1.Players[0]).Target);
            AreTheseEqual(myData1.Players[2], ((PlayerAdvanced)myData1.Players[3]).Target);
            AreTheseEqual(myData1.Locations[0], myData1.Locations[1].Connections[0]);
            AreTheseEqual(myData1.Locations[1], myData1.Locations[0].Connections[0]);
            AreTheseEqual(myData1.Players[0], myData1.Locations[0].Players[0]);
            AreTheseEqual(myData1.Locations[0].Players[1], ((PlayerAdvanced)myData1.Locations[1].Players[1]).Target);
            Console.WriteLine("  Deserialization Results:");
            AreTheseEqual(myData2.Players[0], ((PlayerAdvanced)myData2.Players[1]).Target);
            AreTheseEqual(myData2.Players[1], ((PlayerAdvanced)myData2.Players[0]).Target);
            AreTheseEqual(myData2.Players[2], ((PlayerAdvanced)myData2.Players[3]).Target);
            AreTheseEqual(myData2.Locations[0], myData2.Locations[1].Connections[0]);
            AreTheseEqual(myData2.Locations[1], myData2.Locations[0].Connections[0]);
            AreTheseEqual(myData2.Players[0], myData2.Locations[0].Players[0]);
            AreTheseEqual(myData2.Locations[0].Players[1], ((PlayerAdvanced)myData2.Locations[1].Players[1]).Target);
        }
        static void Advanced_2_SimplePolymorphism()
        {
            ConsoleTitle("SECTION: Simple Polymorphism");

            Console.Write("Preparing all the base data...");
            // create sample object
            PlayerAdvanced player1 = new PlayerAdvanced("Binky", 20, new Weapon("sword", 5), new Armor("robe", 1));
            // create sample object
            PlayerAdvanced player2 = new PlayerAdvanced("Frosty", 20, new Weapon("bow", 3), new Armor("leather", 3));

            player1.SetTarget(player2);
            player2.SetTarget(player1);
            List <Player> players = new List <Player> {
                player1, player2
            };

            JsonSerializerSettings settings1 = new JsonSerializerSettings
            {
                ContractResolver           = new PrivateSetResolver(),
                PreserveReferencesHandling = PreserveReferencesHandling.Objects
            };
            string        json1       = JsonConvert.SerializeObject(players, Formatting.Indented, settings1);
            List <Player> PlayerList2 = (List <Player>)JsonConvert.DeserializeObject <List <Player> >(json1, settings1);

            JsonSerializerSettings settings2 = new JsonSerializerSettings
            {
                ContractResolver           = new PrivateSetResolver(),
                PreserveReferencesHandling = PreserveReferencesHandling.Objects,
                TypeNameHandling           = TypeNameHandling.Auto
            };

            Console.WriteLine("\n  Serializing with 'TypeNameHandling = TypeNameHandling.Auto':");
            string        json2       = JsonConvert.SerializeObject(players, Formatting.Indented, settings2);
            List <object> PlayerList3 = (List <object>)JsonConvert.DeserializeObject <List <object> >(json2, settings2);

            JsonSerializerSettings settings3 = new JsonSerializerSettings
            {
                ContractResolver           = new PrivateSetResolver(),
                PreserveReferencesHandling = PreserveReferencesHandling.Objects,
                TypeNameHandling           = TypeNameHandling.Auto,
                ConstructorHandling        = ConstructorHandling.AllowNonPublicDefaultConstructor
            };

            Console.WriteLine("\n  Serializing with 'TypeNameHandling = TypeNameHandling.Auto':");
            string        json3       = JsonConvert.SerializeObject(players, Formatting.Indented, settings3);
            List <object> PlayerList4 = (List <object>)JsonConvert.DeserializeObject <List <object> >(json2, settings3);


            Console.WriteLine("\n  JSON Datasets:");
            DrawJson(json1);
            DrawJson(json2);
            DrawJson(json3);


            // set 1
            Console.WriteLine("\n  Trying to turn original serialized objects into derived classes from base clase list type:");
            try {
                PlayerAdvanced player3 = (PlayerAdvanced)PlayerList2[0];                 // error here
                PlayerAdvanced player4 = (PlayerAdvanced)PlayerList2[1];
            } catch (InvalidCastException) {
                Console.WriteLine("    Failed to cast from PlayerAdvanced:Player serial data into Player list.");
            }
            // set 2
            PlayerAdvanced player5 = (PlayerAdvanced)PlayerList3[0];
            PlayerAdvanced player6 = (PlayerAdvanced)PlayerList3[1];
            // set 3
            PlayerAdvanced player7 = (PlayerAdvanced)PlayerList4[0];
            PlayerAdvanced player8 = (PlayerAdvanced)PlayerList4[1];

            Console.WriteLine("\n  Source Data:");
            players.ForEach(p => Console.WriteLine($"    {p.ToString()}"));

            Console.WriteLine("\n  Expected Results:");
            AreTheseEqual(player1, player2.Target);
            AreTheseEqual(player2, player1.Target);
            Console.WriteLine("  Data Set 2:");
            Console.WriteLine("    (failed to deserialize)");
            Console.WriteLine("  Data Set 3 (as with earlier examples, this requires default constructor, circular references fail without attribute):");
            AreTheseEqual(player5, player6.Target);
            AreTheseEqual(player6, player5.Target);
            Console.WriteLine("  Data Set 4 (same thing, doesn't require attribute on default constructor):");
            AreTheseEqual(player7, player8.Target);
            AreTheseEqual(player8, player7.Target);
        }