jorgen / json_struct

json_struct is a single header only C++ library for parsing JSON directly to C++ structs and vice versa
Other
422 stars 57 forks source link

It's not a bug. How to count elements of nested array? #15

Closed clabnet closed 3 years ago

clabnet commented 3 years ago

Hi, on this array "vec" : [ { "key" : 4, "value": 1.0 }, { "key" : 5, "value": 2.0 }, { "key" : 6, "value": 3.0 } ] how to know the number of elements ? On this sample, 3. Thank's

jorgen commented 3 years ago

Hi, thank you for the query!

There are multiple ways of finding the size of vec, but I think the easiest and most versatile is to use a std::vector. Please see the following example:

#include <string>
#include <json_struct.h>

const char json[] = R"json(
{
  "vec" : [
    { "key" : 4, "value": 1.0 },
    { "key" : 5, "value": 2.0 },
    { "key" : 6, "value": 3.0 }
  ]
}
)json";

struct VecMember
{
  std::string key;
  double value;

  JS_OBJ(key, value);
};

struct JsonObject
{
  std::vector<VecMember> vec;
  JS_OBJ(vec);
};

int main()
{
    JsonObject obj;
    JS::ParseContext parseContext(json);
    if (parseContext.parseTo(obj) != JS::Error::NoError)
    {
        std::string errorStr = parseContext.makeErrorString();
        fprintf(stderr, "Error parsing struct %s\n", errorStr.c_str());
        return -1;
    }

    fprintf(stdout, "Vec has size %zu\n", obj.vec.size());

    return 0;
}

Hope this answers the question. If it does please close the issue.

clabnet commented 3 years ago

Hi @jorgen , thank you for your fast response. Your reply is very valuable, but my problem is a little too complicated. I try to be more precise. My json_data is contained into config.json file:

{
    "webInterface": "localhost",
    "......"; "......",
    "moduleList": [
        { "id": 0, "name": "MAINTENANCE" },
        { "id": 1, "name": "CORE" },
        { "id": 2, "name": "BLA BLA" }
    ]
}

and my struct is here : https://pastebin.com/embed_js/fp7WYs6c Please note on this code the struct TypeHandler<ModuleList<T>> part and the MODULELIST_COUNT define.

When I read the config.json file,

   std::ifstream ifs(config_file.c_str());
   if (!ifs) {
                throw std::invalid_argument("Can't open app config file " + config_file);
   }
   std::string json_data((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
   spdlog::trace(json_data);

I can't parse securely the entire json into Config struct because I can't know how much elements of array the user can be defined. The user can be define one, two, "n" element on moduleList array.

The final goal: I would remove from the source code the MODULELIST_COUNT define constant, but I don't know.

Sorry for the long post and thank you for your time.

Regards,

jorgen commented 3 years ago

right! ok. so you want to have a static sized buffer, but want to populate it with dynamic data? so I assume you will have som size variable keeping count of how much of the buffer is filled up? Since you have already a type handler we can modify it "by copying the array type handler spesialisation" and make it fit your needs. I have adjusted the example, does this fit the bill? We have some more options if it doesn't do what you want it to :D

#include <string>
#include <json_struct.h>

const char json[] = R"json(
{
  "vec" : [
    { "key" : 4, "value": 1.0 },
    { "key" : 5, "value": 2.0 },
    { "key" : 6, "value": 3.0 }
  ]
}
)json";

struct VecMember
{
  std::string key;
  double value;

  JS_OBJ(key, value);
};

struct ModuleList
{
  enum
  {
    ReservedSize = 16
  };
  VecMember modules[ReservedSize];
  int size = 0;
};

namespace JS
{
template <>
struct TypeHandler<ModuleList>
{
  static inline Error to(ModuleList &to_type, ParseContext &context)
  {
    if (context.token.value_type != Type::ArrayStart)
      return JS::Error::ExpectedArrayStart;

    context.nextToken();
    for (size_t i = 0; i < ModuleList::ReservedSize; i++)
    {
      if (context.error != JS::Error::NoError)
        return context.error;
      if (context.token.value_type == Type::ArrayEnd)
      {
        to_type.size = i;
        break;
      }
      context.error = TypeHandler<VecMember>::to(to_type.modules[i], context);
      if (context.error != JS::Error::NoError)
        return context.error;

      context.nextToken();
    }

    if (context.token.value_type != Type::ArrayEnd)
      return JS::Error::ExpectedArrayEnd;
    return context.error;
  }

  static inline void from(const ModuleList &from_type, Token &token, Serializer &serializer)
  {
    token.value_type = Type::ArrayStart;
    token.value = DataRef("[");
    serializer.write(token);

    token.name = DataRef("");
    for (size_t i = 0; i < from_type.size; i++)
      TypeHandler<VecMember>::from(from_type.modules[i], token, serializer);

    token.name = DataRef("");
    token.value_type = Type::ArrayEnd;
    token.value = DataRef("]");
    serializer.write(token);
  }
};
} // namespace JS

struct JsonObject
{
  ModuleList vec;
  JS_OBJ(vec);
};

int main()
{
    JsonObject obj;
    JS::ParseContext parseContext(json);
    if (parseContext.parseTo(obj) != JS::Error::NoError)
    {
        std::string errorStr = parseContext.makeErrorString();
        fprintf(stderr, "Error parsing struct %s\n", errorStr.c_str());
        return -1;
    }

    fprintf(stdout, "Vec has size %zu\n", obj.vec.size);

    return 0;
}
jorgen commented 3 years ago

Hi! I just added a new type to json_struct called JS::ArrayVariableContent. It is basically the code above but removes the need for a more advanced type handler.

https://github.com/jorgen/json_struct/blob/master/tests/json-struct-array-varlength.cpp

jorgen commented 3 years ago

Can I close this issue now?

clabnet commented 3 years ago

Sorry for delay, tomorrow I send you my solution.

clabnet commented 3 years ago

Many thanks for your proposal, but I have found a short road. I have set MODULELIST_COUNT to 32 (fortunately I know how the max number of elements the user can create) and defined

        std::vector<Module> moduleList;

It work as is.

Good to hear you, and many thanks @jorgen.

Best regards.

clabnet commented 3 years ago

closed