skip to Main Content

I have a program that reads useful information from a .json file while ignoring the rest, and manipulates it and translates it into a form of .json that will be more useful for a future project. However, for some reason, it ignores the nested arrays within the objects and assigns their value in the translated file as a null value. I’ve tried several solutions, but most recently, I get the error:

Newtonsoft.Json.JsonSerializationException: ‘Cannot deserialize the current JSON array (e.g. [1,2,3]) into type JSON_Reformatter.MonsterOld+ActionList’ because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly.’

Here’s a snippet of how the .json used to be formatted:

[

  {
    "slug": "aboleth",
    "name": "Aboleth",
    "size": "Large",
    "type": "aberration",
    "subtype": "",
    "group": null,
    "alignment": "lawful evil",
    "armor_class": 17,
    "armor_desc": "natural armor",
    "hit_points": 135,
    "hit_dice": "18d10+36",
    "speed": {
      "walk": 10,
      "swim": 40
    },
    "strength": 21,
    "dexterity": 9,
    "constitution": 15,
    "intelligence": 18,
    "wisdom": 15,
    "charisma": 18,
    "strength_save": null,
    "dexterity_save": null,
    "constitution_save": 6,
    "intelligence_save": 8,
    "wisdom_save": 6,
    "charisma_save": null,
    "perception": 10,
    "skills": {
      "history": 12,
      "perception": 10
    },
    "damage_vulnerabilities": "",
    "damage_resistances": "",
    "damage_immunities": "",
    "condition_immunities": "",
    "senses": "darkvision 120 ft., passive Perception 20",
    "languages": "Deep Speech, telepathy 120 ft.",
    "challenge_rating": "10",
    "cr": 10.0,
    "actions": [
      {
        "name": "Multiattack",
        "desc": "The aboleth makes three tentacle attacks."
      },
      {
        "name": "Tentacle",
        "desc": "Melee Weapon Attack: +9 to hit, reach 10 ft., one target. Hit: 12 (2d6 + 5) bludgeoning damage. If the target is a creature, it must succeed on a DC 14 Constitution saving throw or become diseased. The disease has no effect for 1 minute and can be removed by any magic that cures disease. After 1 minute, the diseased creature's skin becomes translucent and slimy, the creature can't regain hit points unless it is underwater, and the disease can be removed only by heal or another disease-curing spell of 6th level or higher. When the creature is outside a body of water, it takes 6 (1d12) acid damage every 10 minutes unless moisture is applied to the skin before 10 minutes have passed.",
        "attack_bonus": 9,
        "damage_dice": "2d6",
        "damage_bonus": 5
      },
      {
        "name": "Tail",
        "desc": "Melee Weapon Attack: +9 to hit, reach 10 ft., one target. Hit: 15 (3d6 + 5) bludgeoning damage.",
        "attack_bonus": 9,
        "damage_dice": "3d6",
        "damage_bonus": 5
      },
      {
        "name": "Enslave (3/day)",
        "desc": "The aboleth targets one creature it can see within 30 ft. of it. The target must succeed on a DC 14 Wisdom saving throw or be magically charmed by the aboleth until the aboleth dies or until it is on a different plane of existence from the target. The charmed target is under the aboleth's control and can't take reactions, and the aboleth and the target can communicate telepathically with each other over any distance.nWhenever the charmed target takes damage, the target can repeat the saving throw. On a success, the effect ends. No more than once every 24 hours, the target can also repeat the saving throw when it is at least 1 mile away from the aboleth."
      }
    ],
    "reactions": "",
    "legendary_desc": "The aboleth can take 3 legendary actions, choosing from the options below. Only one legendary action option can be used at a time and only at the end of another creature's turn. The aboleth regains spent legendary actions at the start of its turn.",
    "legendary_actions": [
      {
        "name": "Detect",
        "desc": "The aboleth makes a Wisdom (Perception) check."
      },
      {
        "name": "Tail Swipe",
        "desc": "The aboleth makes one tail attack."
      },
      {
        "name": "Psychic Drain (Costs 2 Actions)",
        "desc": "One creature charmed by the aboleth takes 10 (3d6) psychic damage, and the aboleth regains hit points equal to the damage the creature takes."
      }
    ],
    "special_abilities": [
      {
        "name": "Amphibious",
        "desc": "The aboleth can breathe air and water."
      },
      {
        "name": "Mucous Cloud",
        "desc": "While underwater, the aboleth is surrounded by transformative mucus. A creature that touches the aboleth or that hits it with a melee attack while within 5 ft. of it must make a DC 14 Constitution saving throw. On a failure, the creature is diseased for 1d4 hours. The diseased creature can breathe only underwater."
      },
      {
        "name": "Probing Telepathy",
        "desc": "If a creature communicates telepathically with the aboleth, the aboleth learns the creature's greatest desires if the aboleth can see the creature."
      }
    ],
    "spell_list": [],
    "page_no": 261,
    "img_main": "http://api.open5e.com/static/img/monsters/aboleth.png",
    "document__slug": "wotc-srd",
    "document__title": "Systems Reference Document",
    "document__license_url": "http://open5e.com/legal",
    "document__url": "http://dnd.wizards.com/articles/features/systems-reference-document-srd"
  },
  {
    "slug": "acolyte",
    "name": "Acolyte",
    "size": "Medium",
    "type": "humanoid",
    "subtype": "any race",
    "group": null,
    "alignment": "any alignment",
    "armor_class": 10,
    "armor_desc": null,
    "hit_points": 9,
    "hit_dice": "2d8",
    "speed": { "walk": 30 },
    "strength": 10,
    "dexterity": 10,
    "constitution": 10,
    "intelligence": 10,
    "wisdom": 14,
    "charisma": 11,
    "strength_save": null,
    "dexterity_save": null,
    "constitution_save": null,
    "intelligence_save": null,
    "wisdom_save": null,
    "charisma_save": null,
    "perception": null,
    "skills": {
      "medicine": 4,
      "religion": 2
    },
    "damage_vulnerabilities": "",
    "damage_resistances": "",
    "damage_immunities": "",
    "condition_immunities": "",
    "senses": "passive Perception 12",
    "languages": "any one language (usually Common)",
    "challenge_rating": "1/4",
    "cr": 0.25,
    "actions": [
      {
        "name": "Club",
        "desc": "Melee Weapon Attack: +2 to hit, reach 5 ft., one target. Hit: 2 (1d4) bludgeoning damage.",
        "attack_bonus": 2,
        "damage_dice": "1d4"
      }
    ],
    "reactions": "",
    "legendary_desc": "",
    "legendary_actions": "",
    "special_abilities": [
      {
        "name": "Spellcasting",
        "desc": "The acolyte is a 1st-level spellcaster. Its spellcasting ability is Wisdom (spell save DC 12, +4 to hit with spell attacks). The acolyte has following cleric spells prepared:nn� Cantrips (at will): light, sacred flame, thaumaturgyn� 1st level (3 slots): bless, cure wounds, sanctuary"
      }
    ],
    "spell_list": [ "https://api.open5e.com/spells/light/?format=json", "https://api.open5e.com/spells/sacred-flame/?format=json", "https://api.open5e.com/spells/thaumaturgy/?format=json", "https://api.open5e.com/spells/bless/?format=json", "https://api.open5e.com/spells/cure-wounds/?format=json", "https://api.open5e.com/spells/sanctuary/?format=json" ],
    "page_no": 395,
    "img_main": null,
    "document__slug": "wotc-srd",
    "document__title": "Systems Reference Document",
    "document__license_url": "http://open5e.com/legal",
    "document__url": "http://dnd.wizards.com/articles/features/systems-reference-document-srd"
  },
  {
    "slug": "adult-black-dragon",
    "name": "Adult Black Dragon",
    "size": "Huge",
    "type": "dragon",
    "subtype": "",
    "group": "Black Dragon",
    "alignment": "chaotic evil",
    "armor_class": 19,
    "armor_desc": "natural armor",
    "hit_points": 195,
    "hit_dice": "17d12+85",
    "speed": {
      "walk": 40,
      "fly": 80,
      "swim": 40
    },
    "strength": 23,
    "dexterity": 14,
    "constitution": 21,
    "intelligence": 14,
    "wisdom": 13,
    "charisma": 17,
    "strength_save": null,
    "dexterity_save": 7,
    "constitution_save": 10,
    "intelligence_save": null,
    "wisdom_save": 6,
    "charisma_save": 8,
    "perception": 11,
    "skills": {
      "perception": 11,
      "stealth": 7
    },
    "damage_vulnerabilities": "",
    "damage_resistances": "",
    "damage_immunities": "acid",
    "condition_immunities": "",
    "senses": "blindsight 60 ft., darkvision 120 ft., passive Perception 21",
    "languages": "Common, Draconic",
    "challenge_rating": "14",
    "cr": 14.0,
    "actions": [
      {
        "name": "Multiattack",
        "desc": "The dragon can use its Frightful Presence. It then makes three attacks: one with its bite and two with its claws."
      },
      {
        "name": "Bite",
        "desc": "Melee Weapon Attack: +11 to hit, reach 10 ft., one target. Hit: 17 (2d10 + 6) piercing damage plus 4 (1d8) acid damage.",
        "attack_bonus": 11,
        "damage_dice": "2d10+1d8",
        "damage_bonus": 6
      },
      {
        "name": "Claw",
        "desc": "Melee Weapon Attack: +11 to hit, reach 5 ft., one target. Hit: 13 (2d6 + 6) slashing damage.",
        "attack_bonus": 11,
        "damage_dice": "2d6",
        "damage_bonus": 6
      },
      {
        "name": "Tail",
        "desc": "Melee Weapon Attack: +11 to hit, reach 15 ft., one target. Hit: 15 (2d8 + 6) bludgeoning damage.",
        "attack_bonus": 11,
        "damage_dice": "2d8",
        "damage_bonus": 6
      },
      {
        "name": "Frightful Presence",
        "desc": "Each creature of the dragon's choice that is within 120 feet of the dragon and aware of it must succeed on a DC 16 Wisdom saving throw or become frightened for 1 minute. A creature can repeat the saving throw at the end of each of its turns, ending the effect on itself on a success. If a creature's saving throw is successful or the effect ends for it, the creature is immune to the dragon's Frightful Presence for the next 24 hours."
      },
      {
        "name": "Acid Breath (Recharge 5-6)",
        "desc": "The dragon exhales acid in a 60-foot line that is 5 feet wide. Each creature in that line must make a DC 18 Dexterity saving throw, taking 54 (12d8) acid damage on a failed save, or half as much damage on a successful one.",
        "attack_bonus": 0,
        "damage_dice": "12d8"
      }
    ],
    "reactions": "",
    "legendary_desc": "The dragon can take 3 legendary actions, choosing from the options below. Only one legendary action option can be used at a time and only at the end of another creature's turn. The dragon regains spent legendary actions at the start of its turn.",
    "legendary_actions": [
      {
        "name": "Detect",
        "desc": "The dragon makes a Wisdom (Perception) check."
      },
      {
        "name": "Tail Attack",
        "desc": "The dragon makes a tail attack."
      },
      {
        "name": "Wing Attack (Costs 2 Actions)",
        "desc": "The dragon beats its wings. Each creature within 10 ft. of the dragon must succeed on a DC 19 Dexterity saving throw or take 13 (2d6 + 6) bludgeoning damage and be knocked prone. The dragon can then fly up to half its flying speed."
      }
    ],
    "special_abilities": [
      {
        "name": "Amphibious",
        "desc": "The dragon can breathe air and water."
      },
      {
        "name": "Legendary Resistance (3/Day)",
        "desc": "If the dragon fails a saving throw, it can choose to succeed instead."
      }
    ],
    "spell_list": [],
    "page_no": 281,
    "img_main": null,
    "document__slug": "wotc-srd",
    "document__title": "Systems Reference Document",
    "document__license_url": "http://open5e.com/legal",
    "document__url": "http://dnd.wizards.com/articles/features/systems-reference-document-srd"
  },
  {
    "slug": "adult-blue-dragon",
    "name": "Adult Blue Dragon",
    "size": "Huge",
    "type": "dragon",
    "subtype": "",
    "group": "Blue Dragon",
    "alignment": "lawful evil",
    "armor_class": 19,
    "armor_desc": "natural armor",
    "hit_points": 225,
    "hit_dice": "18d12+108",
    "speed": {
      "walk": 40,
      "burrow": 30,
      "fly": 80
    },
    "strength": 25,
    "dexterity": 10,
    "constitution": 23,
    "intelligence": 16,
    "wisdom": 15,
    "charisma": 19,
    "strength_save": null,
    "dexterity_save": 5,
    "constitution_save": 11,
    "intelligence_save": null,
    "wisdom_save": 7,
    "charisma_save": 9,
    "perception": 12,
    "skills": {
      "perception": 12,
      "stealth": 5
    },
    "damage_vulnerabilities": "",
    "damage_resistances": "",
    "damage_immunities": "lightning",
    "condition_immunities": "",
    "senses": "blindsight 60 ft., darkvision 120 ft., passive Perception 22",
    "languages": "Common, Draconic",
    "challenge_rating": "16",
    "cr": 16.0,
    "actions": [
      {
        "name": "Multiattack",
        "desc": "The dragon can use its Frightful Presence. It then makes three attacks: one with its bite and two with its claws."
      },
      {
        "name": "Bite",
        "desc": "Melee Weapon Attack: +12 to hit, reach 10 ft., one target. Hit: 18 (2d10 + 7) piercing damage plus 5 (1d10) lightning damage.",
        "attack_bonus": 12,
        "damage_dice": "2d10+1d10",
        "damage_bonus": 7
      },
      {
        "name": "Claw",
        "desc": "Melee Weapon Attack: +12 to hit, reach 5 ft., one target. Hit: 14 (2d6 + 7) slashing damage.",
        "attack_bonus": 12,
        "damage_dice": "2d6",
        "damage_bonus": 7
      },
      {
        "name": "Tail",
        "desc": "Melee Weapon Attack: +12 to hit, reach 15 ft., one target. Hit: 16 (2d8 + 7) bludgeoning damage.",
        "attack_bonus": 12,
        "damage_dice": "2d8",
        "damage_bonus": 7
      },
      {
        "name": "Frightful Presence",
        "desc": "Each creature of the dragon's choice that is within 120 ft. of the dragon and aware of it must succeed on a DC 17 Wisdom saving throw or become frightened for 1 minute. A creature can repeat the saving throw at the end of each of its turns, ending the effect on itself on a success. If a creature's saving throw is successful or the effect ends for it, the creature is immune to the dragon's Frightful Presence for the next 24 hours."
      },
      {
        "name": "Lightning Breath (Recharge 5-6)",
        "desc": "The dragon exhales lightning in a 90-foot line that is 5 ft. wide. Each creature in that line must make a DC 19 Dexterity saving throw, taking 66 (12d10) lightning damage on a failed save, or half as much damage on a successful one.",
        "attack_bonus": 0,
        "damage_dice": "12d10"
      }
    ],
    "reactions": "",
    "legendary_desc": "The dragon can take 3 legendary actions, choosing from the options below. Only one legendary action option can be used at a time and only at the end of another creature's turn. The dragon regains spent legendary actions at the start of its turn.",
    "legendary_actions": [
      {
        "name": "Detect",
        "desc": "The dragon makes a Wisdom (Perception) check."
      },
      {
        "name": "Tail Attack",
        "desc": "The dragon makes a tail attack."
      },
      {
        "name": "Wing Attack (Costs 2 Actions)",
        "desc": "The dragon beats its wings. Each creature within 10 ft. of the dragon must succeed on a DC 20 Dexterity saving throw or take 14 (2d6 + 7) bludgeoning damage and be knocked prone. The dragon can then fly up to half its flying speed."
      }
    ],
    "special_abilities": [
      {
        "name": "Legendary Resistance (3/Day)",
        "desc": "If the dragon fails a saving throw, it can choose to succeed instead."
      }
    ],
    "spell_list": [],
    "page_no": 283,
    "img_main": null,
    "document__slug": "wotc-srd",
    "document__title": "Systems Reference Document",
    "document__license_url": "http://open5e.com/legal",
    "document__url": "http://dnd.wizards.com/articles/features/systems-reference-document-srd"
  }
]

Here’s a list of relevant classes:

public class MonsterOld
    {
        public string? name;
        public string? size;
        public string? type;
        public string? subtype;
        public string? alignment;
        public int armor_class;
        public string? armor_desc;
        public int hit_points;
        public string? hit_dice;
        public Speed? speed;
        public int? strength;
        public int? dexterity;
        public int? constitution;
        public int? intelligence;
        public int? wisdom;
        public int? charisma;
        public int? strength_save;
        public int? dexterity_save;
        public int? constitution_save;
        public int? intelligence_save;
        public int? wisdom_save;
        public int? charisma_save;
        public int? perception;
        public Skills? skills;
        public string? damage_vulnerabilities;
        public string? damage_resistances;
        public string? damage_immunities;
        public string? condition_immunities;
        public string? senses;
        public string? languages;
        public float cr;

        public class ActionList
        {
            public List<Actions>? actions;
        }

        public class ReactionList
        {
            public List<Actions>? reactions;
        }

        public string? legendary_desc;
        public class LegActionList
        {
            public List<Actions>? legActions;
        }
        public class SpecAbList
        {
            public List<Actions>? specAbs;
        }

        public ActionList? actions;
        public ReactionList? reactions;
        public LegActionList? legendary_actions;
        public SpecAbList? special_abilities;


    }
    [Serializable]
    public class MonsterNew
    {
        public string? name;
        public string? size;
        public string? type;
        public string? subtype;
        public string? alignment;
        public int ac;
        public string? armor_desc;
        public int hp;
        public string? hit_dice;
        public Speed? speed;
        public int? stren;
        public int? dex;
        public int? con;
        public int? intell;
        public int? wis;
        public int? cha;
        public int? strenMod;
        public int? dexMod;
        public int? conMod;
        public int? intellMod;
        public int? wisMod;
        public int? chaMod;
        public int? perception;
        public int? profBonus;
        public Skills skills;
        public string[]? condition_immunities;
        public string[]? damage_vulnerabilities;
        public string[]? damage_resistances;
        public string[]? damage_immunities;
        public string[]? senses;
        public string[]? languages;
        public float cr;

        public List<Actions>? actions;
        public List<Actions>? reactions;
        public string? legendary_desc;
        public int? numLegActions;
        public List<Actions>? legendary_actions;
        public List<Actions>? special_abilities;

        public MonsterNew(Skills skills)
        {
            this.skills = skills;
        }
    }
    [Serializable]
    public class Actions
    {
        public string? name;
        public string? desc;
        public string? damage_dice;
        public int? attack_bonus;
        public int? damage_bonus;
        
        public bool? aoe;////
        public string? aoe_type;////
        public int? aoe_size;////
        public int? DC;////
        public int? daily_uses;////
        public Range? range;////
        public bool? half_on_save;//
        public int? leg_action_cost;////
    }

The method to deserialize:

static List<MonsterOld> deserializeInitialFile()
        {
            string fileName = @"S:VTT SIMVTTsimAssetsDatabase json filesMonsters(SRD).json";
            if (File.Exists(fileName))
            {
                var monsters = JsonConvert.DeserializeObject<List<MonsterOld>>(File.ReadAllText(fileName));

                return monsters;
            }
            return (null);
        }

And the function to reformat the data, should it be relevant

public List<MonsterNew> Reformat(List<MonsterOld> oldMonsters)
        {
            List<MonsterNew> outgoing = new List<MonsterNew>();
            foreach (MonsterOld mOld in oldMonsters)
            {

                MonsterNew mNew = new MonsterNew(mOld.skills);
                mNew.name = mOld.name;
                mNew.size = mOld.size;
                mNew.type = mOld.type;
                mNew.subtype = mOld.subtype;
                mNew.alignment = mOld.alignment;
                mNew.ac = mOld.armor_class;
                mNew.armor_desc = mOld.armor_desc;
                mNew.hp = mOld.hit_points;
                mNew.hit_dice = mOld.hit_dice;
                mNew.speed = mOld.speed;
                mNew.stren = mOld.strength;
                mNew.dex = mOld.dexterity;
                mNew.con = mOld.constitution;
                mNew.intell = mOld.intelligence;
                mNew.wis = mOld.wisdom;
                mNew.cha = mOld.charisma;
                mNew.strenMod = calcMod(mOld.strength);
                mNew.dexMod = calcMod(mOld.dexterity);
                mNew.conMod = calcMod(mOld.constitution);
                mNew.intellMod = calcMod(mOld.intelligence);
                mNew.wisMod = calcMod(mOld.wisdom);
                mNew.chaMod = calcMod(mOld.charisma);
                if (mOld.strength_save != null)
                    mNew.skills.stren_save = true;
                if (mOld.dexterity_save != null)
                    mNew.skills.dex_save = true;
                if (mOld.constitution_save != null)
                    mNew.skills.con_save = true;
                if (mOld.intelligence_save != null)
                    mNew.skills.intel_save = true;
                if (mOld.wisdom_save != null)
                    mNew.skills.wis_save = true;
                if (mOld.charisma_save != null)
                    mNew.skills.cha_save = true;
                mNew.perception = mOld.perception;
                mNew.profBonus = calcProfBonus(mOld.cr);
                mNew.condition_immunities = splitAtComma(mOld.condition_immunities);
                mNew.damage_immunities = splitAtComma(mOld.damage_immunities);
                mNew.damage_resistances = splitAtComma(mOld.damage_resistances);
                mNew.damage_vulnerabilities = splitAtComma(mOld.damage_vulnerabilities);
                mNew.senses = splitAtComma(mOld.senses);
                mNew.languages = splitAtComma(mOld.languages);
                mNew.cr = mOld.cr;

                mNew.actions = translateNewActionMembers(mOld.actions.actions);
                mNew.reactions = translateNewActionMembers(mOld.reactions.reactions);
                mNew.numLegActions = getLegActions(mOld.legendary_desc);
                mNew.legendary_desc = mOld.legendary_desc;
                mNew.legendary_actions = translateNewActionMembers(mOld.legendary_actions.legActions);
                mNew.special_abilities = translateNewActionMembers(mOld.special_abilities.specAbs);
                

                

                outgoing.Add(mNew);
            }
            return outgoing;

        }

Primarily, I need to be able to be able to read the information from the action, reaction, legendary_action, and special_abilities arrays into the program. I’m still new to C#, so the simpler any solution can be made, the better.

2

Answers


  1. According to the error, your input json looks like this

    {
       ...
       "actions": [
        {...},
        {...}
       ]
       ...
    }
    

    and according to you model, json deserializer would expect:

    {
       ...
       "actions": {
          "actions": [
          {...},
          {...}
          ]
       }
       ...
    }
    

    That’s because you put List<Actions>? actions inside the class ActionList.

    Since there are no other members inside ActionList class, the easiest solution for you would be to get rid of wrapper classes like ActionList and change the declaration of MonsterOld to

    public class MonsterOld
        {
            ...
            public List<Actions>? actions;
            //and if other properties have the same issue
            public List<Actions>? reactions;
            public List<Actions>? legendary_actions;
            public List<Actions>? special_abilities;
        }
    
    Login or Signup to reply.
  2. You really, really, really need to read up on how to create a Minimal Reproduceable Example. You have a ton of sludge in your code, (including two (count’em, two) Monster classes. You also have a bunch of quadruple slash tokens ) //// with no explanation as to what they mean.

    What I did to get it working…

    1. Changed all your fields to public properties (in all your classes)
      Changing:
        public class MonsterOld
        {
            public string? name;
            public string? size;
            public string? type;
            // etc.
    

    To:

        public class MonsterOld
        {
            public string? name { get; set; }
            public string? size { get; set; }
            public string? type { get; set; }
            // etc.
    
    1. Added the two missing classes:
        public class Speed
        {
            public Dictionary<string, int>? TheSpeed { get; set; }
        }
    
        public class Skills
        {
            public Dictionary<string, int>? TheSkills { get; set; }
        }
    
    1. Removed the definitions of ActionList, ReactionList, LegActionList and SpecAbList

    2. Changed the associated properties in the MonsterOld class to:

        public List<Actions>? actions { get; set; }
        public List<Actions>? reactions { get; set; }
        public List<Actions>? legendary_actions { get; set; }
        public List<Actions>? special_abilities { get; set; }
    

    At that point, I was able to run this code without any exceptions:

    var json = File.ReadAllText("TheJson.json");
    var result = JsonConvert.DeserializeObject<List<MonsterOld>>(json);
    

    The deserialized class instances include non-null actions, legendary_actions etc.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search