Search code examples
c++jsonnlohmann-json

Why am I getting the error "Error parsing JSON: [json.exception.type_error.302] type must be array, but is object"


The following function (or part of it, I will only add the part that is relevant to the problem) is supposed to load a UI configuration from a .json file for a simple game engine I am making (this is all custom). I am using the Nlohmann JSON library for modern C++.

void UIManager::loadUiConfig(SceneManager& sceneManager){
    std::ifstream uiFile("..\\resources\\ui\\ui.json");
    if(!uiFile.is_open()){
        std::cerr << "Failed to load ui configuration file" << std::endl;
        exit(EXIT_FAILURE);
    }

    json uiConfig;
    try{
        uiFile >> uiConfig;
    }catch(const json::parse_error& e){
        std::cerr << e.what() << std::endl;
        exit(EXIT_FAILURE);
    }

    StructScenes scenesData;
    try{
        if(!uiConfig.contains("scenes")) throw std::runtime_error("scenes is not found");
        if(uiConfig["scenes"].is_null()) throw std::runtime_error("scenes is empty/null");
        scenesData = uiConfig.template get<StructScenes>();

    } catch (const std::runtime_error& e) {
        std::cerr << "Runtime Error: " << e.what() << std::endl;
        exit(EXIT_FAILURE);
    }catch(const json::type_error& e){
        std::cerr << "Error parsing JSON: " << e.what() << std::endl;
        exit(EXIT_FAILURE);
    }
    //rest of function
}

These are the structs I defined to hold the data from the .json file:

#ifndef SERIALIZER_CLASS_H
#define SERIALIZER_CLASS_H

#include <glad/glad.h>
#include <vector>
#include <string>
#include <unordered_map>
#include <EventHandler/EventType.h>
#include <Json/json.hpp>
#include <ui/ui.h>

struct StructAppearance{
    GLfloat posX, posY;
    GLfloat width, height;
    GLfloat color[4];
    GLfloat texturePosX, texturePosY;
    std::string textureImagePath;
    int renderType;

    NLOHMANN_DEFINE_TYPE_INTRUSIVE(StructAppearance, posX, posY, width, height, color, texturePosX, texturePosY, textureImagePath, renderType);
};

struct StructButton{

    std::string name;
    std::vector<std::pair<EventType, std::string>> events;
    StructAppearance appearance;
    Shapes shape;
    NLOHMANN_DEFINE_TYPE_INTRUSIVE(StructButton, name, events, appearance, shape);
};

struct StructScene {
    std::string name;
    std::vector<StructButton> sceneMembers;
    std::string vertexShaderPath;
    std::string fragmentShaderPath;
    GLfloat backgroundColor[4];
    int posSize, colorSize, texSize;

    NLOHMANN_DEFINE_TYPE_INTRUSIVE(StructScene, sceneMembers, vertexShaderPath, fragmentShaderPath, backgroundColor, posSize, colorSize, texSize);
};

struct StructScenes{
    std::vector<StructScene> scenes;
    NLOHMANN_DEFINE_TYPE_INTRUSIVE(StructScenes, scenes);
};

#endif

The entry structure (or root structure) is StructScenes, which just holds a std::vector<StructScene> of all the scenes available (this is just a method I am using to create menus in my game).

The problem is at the part where I want to de-serialize my .json data into the StructScenes. It keeps displaying this error:

Error parsing JSON: [json.exception.type_error.302] type must be array, but is object

To be specific, the error is occuring on this line:

scenesData = uiConfig.template get<StructScenes>();

I tried to instead make a direct std::vector<StructScene> but the same issue happened where I got the aforementioned error, instead of actually creating the scenes and filling them with the respective buttons.

EDIT:since people are asking for the ui.json file, here is the sample one I am using:

{
  "scenes": [
    {
      "name": "Main Menu",
      "sceneMembers": [
        {
          "name": "Play",
          "events": [
            {
              "type": "onclick",
              "action": "settings"
            }
          ],
          "appearance": {
            "posX": -0.9,
            "posY": 0.1,
            "width": 0.4,
            "height": 0.3,
            "color": [0, 0, 0, 0],
            "texturePosX": 0,
            "texturePosY": 0,
            "textureImagePath": "..\\resources\\textures\\play.png",
            "renderType": 1
          },
          "shape": "rectangle"
        }
      ],
      "vertexShaderPath": "..\\resources\\Shaders\\default.vert",
      "fragmentShaderPath": "..\\resources\\Shaders\\default.frag",
      "backgroundColor": [1.0, 1.0, 1.0, 1.0],
      "posSize": 2,
      "colorSize": 4,
      "texSize": 2
    }
  ]
}

Solution

  • Based on your provided json file, the events field should be an Array of Object

    "events": [
      {
        "type": "onclick",
        "action": "settings"
      }
    ],
    

    Therefore the events member of this struct is not correct

    struct StructButton {
      std::string name;
      std::vector<std::pair<EventType, std::string>> events;
      StructAppearance appearance;
      Shapes shape;
      NLOHMANN_DEFINE_TYPE_INTRUSIVE(StructButton, name, events, appearance, shape);
    };
    

    It should be another struct which will be an json object

    struct StructEvent {
      std::string type;
      std::string action;
      NLOHMANN_DEFINE_TYPE_INTRUSIVE(StructEvent, type, action);
    };
    

    then you would update the Button member

    struct StructButton {
      std::string name;
      std::vector<StructEvent> events;
      StructAppearance appearance;
      Shapes shape;
      NLOHMANN_DEFINE_TYPE_INTRUSIVE(StructButton, name, events, appearance, shape);
    };