Parse JSON Boolean from subclass [Solved]

Hello there again. I am still working on the same project that I set out to deal the last time I posted, which is an RPG, and I have encountered another problem.

After getting the basic stuff worked, I tried making it so that I could implement a shop and merchants, by setting things up, so I changed up the code to the Item class and its three subclasses, which are weapons, armor, and potions to include a price, which is an integer, and whether the object could be sold, which is presented as a boolean.

However, after making those changes and editing the JSON files, my program could no longer locate the files, which meant I had to present my files paths as hard typed URLs to see if I could get to them (likewise making me add yet more do ... catch blocks), and then I found out that there might be a problem with how I am parsing the JSON.

To see how went, I created a test file with just the Item class, created an object conforming to the class, and finally encoding it to JSON, and everything came back as expected, with the name property, the report property, the price, and the boolean value.

Unfortunately, due to how subclasses do not have the kind of convenience that structs and classes have, in that I only need to specify coding keys to conform to Codable, I had to to specify the encoding and decoding initializers myself.

How am I supposed to retrieve true or false values from JSON conforming to the specs of a subclass?

Right now, my code for all three subclasses looks similar to this, though they each have properties the others do not.

class Weapon:Item {
	var damage: Int!

	enum WeaponCodingKeys: String, CodingKey {
        case name, report, damage, price, sellable
    }

    init (name: String!, report: String!, damage: Int!, price: Int!, sellable: Bool!) {
		self.damage = damage
        super.init(name: name, report: report, price: price, sellable: sellable)
	}

	required convenience init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: WeaponCodingKeys.self)
        let n = try values.decode(String.self, forKey: .name)
        let r = try values.decode(String.self, forKey: .report)
        let d = try values.decode(Int.self, forKey: .damage)
        let p = try values.decode(Int.self, forKey: .price)
        let s = try values.decode(Bool.self, forKey: .sellable)
        
        self.init(name: n, report: r, damage: d, price: p, sellable: s)
    }
    
    override func encode( to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: WeaponCodingKeys.self)
        try container.encode(self.name, forKey: .name)
        try container.encode(self.report, forKey: .report)
        try container.encode(self.damage, forKey: .damage)
        try container.encode(self.price, forKey: .price)
        try container.encode(self.sellable, forKey: .sellable)
    }
}

Every time I had the above code run, via the usual method of turning json into , the program would execute a print statement that I added to the catch statement, saying that the content could not read, suggesting it had something to do with how the content is being parsed, but I am not sure how I am supposed to get any more details on where it failed, resulting in the arrays that the data is ultimately loaded into being 0 elements in size. I am pretty sure it has to do with the boolean value and how I am trying to get it, as other value type did not pose a problem before.

As this mostly involves the JSON data involved with the process, here are the contents of the JSON files.

areas.json:

{
    "areas": [
        {
          "name": "Eastern Coast Beach",
          "dialogue": {
              "report": "A beach with beige colored sand, residing next to a red ocean. (Enter '0' to access player menu)",
              "choices": [
                  "Comb the beach",
                  "Head inland",
                  "Head out to sea"
              ]

          },

          "items": {
              "weapons": [
                  {
                      "name": "Pebble",
                      "report": "an ordinary looking pebble.",
                      "damage": 1,
                      "price": 1,
                      "sellable": true
                  }

              ],
              "armor": [

              ],
              "items": [

              ]

          },
          "monsters": [

          ],
          "doors": [

          ]

        },
        {
            "name": "Eastern Jungle",
            "dialogue": {
                "report": "A place covered in trees and bushes as far as the eye can see. (Enter '0' to access player menu)",
                "choices": [
                    "Explore",
                    "Go into the plains",
                    "Go to the beach"
                ]
  
            },
  
            "items": {
                "weapons": [
                    {
                        "name": "stick",
                        "report": "an oakwood stick that is no longer than a magician's wand.",
                        "damage": 1,
                        "price": 1,
                        "sellable": true
                    }
  
                ],
                "armor": [
                    {
                        "name": "Plain Shirt",
                        "report": "an average looking shirt.",
                        "defense": 1,
                        "slot": "torso",
                        "price": 2,
                        "sellable": true
                    },
                    {
                        "name": "Ragged pants",
                        "report": "pair of pants that have holes and other signs of wear.",
                        "defense": 1,
                        "slot": "legs",
                        "price": 2,
                        "sellable ":true
                    },
                    {
                        "name": "Farmer's Hat",
                        "report": "a hat typically associated with farmers.",
                        "defense": 1,
                        "slot": "head",
                        "price": 3,
                        "sellable": true
                    }
  
                ],
                "items": [
                    {
                        "name": "Heal Potion",
                        "report": "potion that can restore 10 points of health.",
                        "amount": 10,
                        "price": 5,
                        "sellable": true
                    }
  
                ]
  
            },
            "monsters": [
  
            ],
            "doors": [
  
            ]
  
          },
          {
            "name": "Eastern Plains",
            "dialogue": {
                "report": "a place with very little plant life, aside from grass that almost looks dead. (Enter '0' to access player menu)",
                "choices": [
                    "Explore",
                    "Head into forest",
                    "Head to the nearest village",
                    "Go into the desert"
                ]
  
            },
  
            "items": {
                "weapons": [
  
                ],
                "armor": [
  
                ],
                "items": [
  
                ]
  
            },
            "monsters": [
  
            ],
            "doors": [
  
            ]
  
          },
          {
            "name": "Central Desert",
            "dialogue": {
                "report": "A barren wasteland, with carcasses of large beasts, such as elephants, a few different species of cacti here and there, and a cave that is barely noticeable. (Enter '0' to access player menu)",
                "choices": [
                    "Explore",
                    "Bang hands against sand",
                    "Head to the western plains",
                    "Explore the mysterious cave"
                ]
  
            },
  
            "items": {
                "weapons": [
  
                ],
                "armor": [
  
                ],
                "items": [
                    {
                        "name": "Empty Canteen",
                        "report": "canteen that has been left dry by the previous owner",
                        "price": 10,
                        "sellable": true
                    }
  
                ]
  
            },
            "monsters": [
  
            ],
            "doors": [
  
            ]
  
          }
    ]
}

enemies:

{
    "enemies": [
        {
            "name": "Farmer",
            "level": 2,
            "attack": 6,
            "defense": 2,
            "agility": 2,
            "maxHp": 16,
            "hp": 16,
            "exp": 0,
            "evasion": 2,
            "gold": 0,
            "gear": {
                "head": {
                    "name": "Farmer's Hat",
                    "report": "a hat typically associated with farmers.",
                    "defense": 1,
                    "slot": "head",
                    "price": 3,
                    "sellable": true
                },
                "torso": {
                    "name": "Plain Shirt",
                    "report": "an average looking shirt.",
                    "defense": 1,
                    "slot": "torso",
                    "price": 2,
                    "sellable": true
                },
                "legs": {
                    "name": "Ragged pants",
                    "report": "pair of pants that have holes and other signs of wear.",
                    "defense": 1,
                    "slot": "legs",
                    "price": 2,
                    "sellable ":true
                },
                "weapon":  {
                    "name": "Pitchfork",
                    "report": "average looking pitchfork.",
                    "damage": 2,
                    "price": 4,
                    "sellable": true
                }
            },
            "inventory": {
                "weapons": [
                    {
                        "name": "Pitchfork",
                        "report": "average looking pitchfork.",
                        "damage": 2,
                        "price": 4,
                        "sellable": true
                    }

                ],
                "armor": [
                    {
                        "name": "Plain Shirt",
                        "report": "an average looking shirt.",
                        "defense": 1,
                        "slot": "torso",
                        "price": 2,
                        "sellable": true
                    },
                    {
                        "name": "Farmer's Hat",
                        "report": "a hat typically associated with farmers.",
                        "defense": 1,
                        "slot": "head",
                        "price": 3,
                        "sellable": true
                    },
                    {
                        "name": "Ragged pants",
                        "report": "pair of pants that have holes and other signs of wear.",
                        "defense": 1,
                        "slot": "legs",
                        "price": 2,
                        "sellable ":true
                    }
                ],
                "items": [

                ]
            },
            "area": "Eastern Plains"
        },
        {
            "name": "Crab",
            "level": 1,
            "attack": 2,
            "defense": 4,
            "agility": 5,
            "maxHp": 4,
            "hp": 4,
            "exp": 0,
            "evasion": 2,
            "gold": 0,
            "gear": {
            },
            "inventory": {
                "weapons": [

                ],
                "armor": [
                ],
                "items": [

                ]
            },
            "area": "Eastern Coast Beach"
        },
        {
            "name": "Turtle",
            "level": 1,
            "attack": 4,
            "defense": 5,
            "agility": 1,
            "maxHp": 8,
            "hp": 8,
            "exp": 0,
            "evasion": 1,
            "gold": 0,
            "gear": {
            },
            "inventory": {
                "weapons": [

                ],
                "armor": [
                ],
                "items": [

                ]
            },
            "area": "Eastern Coast Beach"
        },
        {
            "name": "Sea Lion",
            "level": 1,
            "attack": 6,
            "defense": 2,
            "agility": 1,
            "maxHp": 7,
            "hp": 7,
            "exp": 0,
            "evasion": 3,
            "gold": 0,
            "gear": {
            },
            "inventory": {
                "weapons": [
                ],
                "armor": [
                ],
                "items": [
                ]
            },
            "area": "Eastern Coast Beach"
        },
        {
            "name": "Anaconda",
            "level": 1,
            "attack": 3,
            "defense": 1,
            "agility": 6,
            "maxHp": 8,
            "hp": 8,
            "exp": 0,
            "evasion": 5,
            "gold": 0,
            "gear": {
            },
            "inventory": {
                "weapons": [
                ],
                "armor": [
                ],
                "items": [
                ]
            },
            "area": "Eastern Jungle"
        },
        {
            "name": "Gorilla",
            "level": 1,
            "attack": 8,
            "defense": 6,
            "agility": 2,
            "maxHp": 10,
            "hp": 10,
            "exp": 0,
            "evasion": 6,
            "gold": 0,
            "gear": {
            },
            "inventory": {
                "weapons": [
                ],
                "armor": [
                ],
                "items": [
                ]
            },
            "area": "Eastern Jungle"
        },
        {
            "name": "Headhunter",
            "level": 1,
            "attack": 2,
            "defense": 2,
            "agility": 7,
            "maxHp": 8,
            "hp": 8,
            "exp": 0,
            "evasion": 2,
            "gold": 0,
            "gear": {
                "head": {
                    "name": "Tiki Mask",
                    "report": "a Mask that has a face similiar to a tiki",
                    "defense": 2,
                    "slot": "head",
                    "price": 5,
                    "sellable": true
                },
                "legs": {
                    "name": "Grass Skirt",
                    "report": "A skirt made of grass.",
                    "defense": 1,
                    "slot": "legs",
                    "price": 1,
                    "sellable": true
                },
                "weapon":  {
                    "name": "Blow Dart",
                    "report": "blow dart made out of bamboo.",
                    "damage": 1,
                    "price": 2,
                    "sellable": true
                }
            },
            "inventory": {
                "weapons": [
                    {
                        "name": "Blow Dart",
                        "report": "blow dart made out of bamboo.",
                        "damage": 1,
                        "price": 2,
                        "sellable": true
                    }

                ],
                "armor": [
                    {
                        "name": "Tiki Mask",
                        "report": "a Mask that has a face similiar to a tiki",
                        "defense": 2,
                        "slot": "head",
                        "price": 5,
                        "sellable": true
                    },
                    {
                        "name": "Grass Skirt",
                        "report": "A skirt made of grass.",
                        "defense": 1,
                        "slot": "legs",
                        "price": 1,
                        "sellable": true
                    }
                ],
                "items": [

                ]
            },
            "area": "Eastern Jungle"
        },
        {
            "name": "Bandit",
            "level": 2,
            "attack": 8,
            "defense": 9,
            "agility": 6,
            "maxHp": 18,
            "hp": 18,
            "exp": 0,
            "evasion": 4,
            "gold": 0,
            "gear": {
                "torso": {
                    "name": "Leather Mail",
                    "report": "mail made of leather.",
                    "defense": 3,
                    "slot": "torso",
                    "price": 9,
                    "sellable": true
                },
                "legs": {
                    "name": "Leather Pants",
                    "report": "pair of pants made of leather.",
                    "defense": 3,
                    "slot": "legs",
                    "price": 10,
                    "sellable": true
                },
                "weapon":  {
                    "name": "Bronze Knife",
                    "report": "average looking pitchfork.",
                    "damage": 4,
                    "price": 15,
                    "sellable": true
                }
            },
            "inventory": {
                "weapons": [
                    {
                        "name": "Bronze Knife",
                        "report": "average looking pitchfork.",
                        "damage": 4,
                        "price": 15,
                        "sellable": true
                    }
                ],
                "armor": [
                    {
                        "name": "Leather Mail",
                        "report": "mail made of leather.",
                        "defense": 3,
                        "slot": "torso",
                        "price": 9,
                        "sellable": true
                    },
                    {
                        "name": "Leather Pants",
                        "report": "pair of pants made of leather.",
                        "defense": 3,
                        "slot": "legs",
                        "price": 10,
                        "sellable": true
                    }
                ],
                "items": [

                ]
            },
            "area": "Eastern Plains"
        },
        {
            "name": "Grizzly Bear",
            "level": 2,
            "attack": 10,
            "defense": 8,
            "agility": 10,
            "maxHp": 22,
            "hp": 22,
            "exp": 0,
            "evasion": 2,
            "gold": 0,
            "gear": {
            },
            "inventory": {
                "weapons": [
                ],
                "armor": [
                ],
                "items": [
                ]
            },
            "area": "Eastern Plains"
        }
    ]
}

These two files are loaded via structs in the Area class and Character class respectively that load and save arrays of each type.

/cc @itaiferber

I looked at my question again recently, and noticed that the JSON key "sellable" had a space between the 'e' and the ' " ' (spaces were added here to clearly denote the double quote character), so I went through it with "find and replace" and fixed those instances, and now things seem to be running fine, though I did not think that would solve my issue

Kind of weird my editor, which is Visual Studio Code, did not notice that.

Looks like you managed to find the error before I got a chance to see this — glad you caught it! In the future, it'll also likely be helpful to include the error message verbatim to make it easier for others to spot what's going on.

I’ll keep that in mind, though not sure how I would go through it, since I was using a Mac VM (Swift code compiles, but does not run on Ubuntu 18.04), and browsers there, particularly on the host machine I have, do not work out too well, nor does the maker provide guest additions for it.

So far, all my errors are the usual “unexpected nil while unwrapping Instruction 4”, even though value was not actually nil, which are not exactly helpful to begin with, unless “instruction 4” means anything.

Ah. okay. From

it seemed to me that you were able to catch and present the DecodingError. The nil unwrapping message is indeed less helpful.