【问题标题】:convert the input of this Json file to a C struct将此 Json 文件的输入转换为 C 结构
【发布时间】:2019-09-11 17:03:28
【问题描述】:

我有一个名为 name.json 的 Json 文件 可以在下面看到这样一个 Json 文件的示例

{
  "set": 5,
  "low": 0,
  "draw_set": "0.1 up to 0.3",
  "Wupet": "Hold",
  "": null
}

但也有可能是其他时候 Json 文件有另一个结构。

{
  "set": 5,
  "low": 0,
  "draw_set": "0.1 up to 0.3",
  "W_set": "Ramp 1.5 ∞C/min",
  "Wset": 0,
  "Wupet": "Hold",
  "": null
}

我想将此 Json 文件的输入(每个文件中的属性及其类型可能不同)转换为 C 结构,其中结构会自动检测 Json 文件中有哪些属性(及其类型)。 “”中的属性“”:null(可以给随机属性名)

然后我想自动将Json值的值赋给对象Book1的结构体。

我的代码计划

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct Books {
   float  set[50];
   int  low[50];
   char  draw_set[100];
   char  Wupet[100];
};

int main( ) {
    /* Read name.Json file */


   struct Books Book1;        /* Declare Book1 of type Book */
   /* Convert content Json to Struct Book1 with it's actual values*/

   return 0;
}

编辑: 如果 Json 文件中存在“Wset”和“W_set”键,我不想忽略它们。 Json 文件的键随时都在改变(未来可能会出现许多未知属性)。钥匙的数量不能保证。保证“多行拆分”属性?应为空键名称指定一个随机名称(可以出现多个键,如空键名称) 应自动检测和创建结构的属性类型。 Json 文件包含单个记录(对象)

我想在运行时处理这些结构。

【问题讨论】:

  • 好的;这是一个好的开始。现在,你接下来要做什么? (我们将帮助您解决您发现的问题;我们不会简单地为您做作业或编写代码。)。您打算忽略"Wset""W_set" 键吗?键的顺序是否有保证?密钥数量是否有保证?是否保证了“多行拆分”属性?键名为空的字段需要做什么?
  • 您可以创建自己的函数来读取 json 文件(这有点复杂),也可以使用任何现有的 C 库来读取文件。我建议选择第二个选项。
  • 可以在https://json.org/的首页底部找到大约18个解析JSON的C库列表
  • 如果 JSON 的结构可以变化,你需要一个更动态的 C 结构,你不能只是硬编码特定的成员名称。上面提到的 JSON 库可能有一些你可以使用的东西,而不是定义你自己的结构。
  • 您的结构无法像"Wset" 这样存储键,因此不足以表示数据文件中的内容。我们不能说这些密钥对您至关重要;您的数据结构暗示它们并不重要。不过,这就是为什么您需要使用库而不是自己动手的原因(除非您手头有很多时间)。我正在滚动我自己的图书馆;一两年来,我一直在做这件事,时不时地(大部分)。这很有趣。

标签: c json struct


【解决方案1】:

结构的属性类型应该是自动检测和创建的。

如果没有一些古怪的宏,您将无法做到这一点。 C 中结构的字段在编译时是固定的。

自动填充你的结构需要能够找出你的结构的字段和类型:reflection。 C 没有反射。您可以编写一些巧妙的宏,但对于这么简单的事情可能不值得。

如果没有一些古怪的宏,基于 JSON 自动向结构添加字段也是不可能的。

相反,我们会将 JSON 解析为灵活的数据结构,您可以随心所欲地使用它。如果要将它们全部放入一个结构中,则该结构必须包含所有可能的字段。有techniques to write extend structs,所以你可以有一个基本的Book,然后是针对不同类型书籍的附加扩展结构。

JSON-Glib 提供两者。它将 JSON 解析为 JsonNodes,可以是 JsonArrays 或 JsonObjects。然后你可以对他们做任何你想做的事情。


在我们这样做之前,结构需要一些工作。

struct Books {
    gint64      set;
    gint64      low;
    const char  *draw_set;
    const char  *wupet;
};

set 不是浮点数,而是整数。

我们使用 GLib 类型来匹配 GLib 库返回的内容并避免大数字溢出。

在读取输入时应避免使用固定大小的缓冲区,它们很容易溢出。相反,JSON-Glib 可以分配正确的内存量。我们将复制它并存储一个指向它的指针。


这是一个使用 JSON-Glib 解析 JSON 文件并手动填充结构的示例。

#include <stdlib.h>
#include <stdio.h>
#include <glib-object.h>
#include <json-glib/json-glib.h>

struct Books {
    gint64      set;
    gint64      low;
    char        *draw_set;
    char        *wupet;
};

int main(int argc, char** argv) {
    GError *error = NULL;

    if (argc < 2) {
        printf("Usage: test <filename.json>\n");
        return EXIT_FAILURE;
    }

    // Parse the JSON from the file
    JsonParser *parser = json_parser_new();
    json_parser_load_from_file(parser, argv[1], &error);
    if(error) {
        printf("Unable to parse `%s': %s\n", argv[1], error->message);
        g_error_free(error);
        g_object_unref(parser);
        return EXIT_FAILURE;
    }

    // Get the root
    JsonNode *root = json_parser_get_root(parser);

    // Turn the root into a JSON object
    JsonObject *stuff = json_node_get_object(root);

    // Get each object member and assign it to the struct.
    struct Books book = {
        .set = json_object_get_int_member(stuff, "set"),
        .low = json_object_get_int_member(stuff, "low"),
        // Duplicate the strings to avoid pointing to memory inside the parser.
        .draw_set = g_strdup(json_object_get_string_member(stuff, "draw_set")),
        .wupet = g_strdup(json_object_get_string_member(stuff, "Wupet"))
    };

    printf(
        "set = %ld, low = %ld, draw_set = '%s', wupet = '%s'\n",
        book.set, book.low, book.draw_set, book.wupet
    );

    // We're finished working with the parser. Deallocate the
    // parser and all the memory it has allocated, including
    // the nodes.
    g_object_unref(parser);

    return EXIT_SUCCESS;
}

【讨论】:

  • @Schwern 我收到错误:从不兼容的指针类型 [-Wincompatible-pointer-types] json_object_unref(root) 传递“json_object_unref”的参数 1;在 /usr/include/json-glib-1.0/json-glib/json-glib.h:29:0 包含的文件中,来自 test.c:4:/usr/include/json-glib-1.0/json-glib /json-types.h:314:23: 注意: 预期的'JsonObject * {aka struct _JsonObject *}' 但参数的类型是'JsonNode * {aka struct _JsonNode *}' void json_object_unref (JsonObject *object); ld:demo.json: 文件格式无法识别;视为链接描述文件
  • @jenniferruurs 我犯了两次错误。第一个错误是rootJsonNode,所以它应该是json_node_unref,不知道为什么json_object_unref 起作用了,但它不再起作用了。可能是更新的版本。第二个错误是释放节点是不必要的,释放解析器也会释放它创建的所有节点。我已经更新了答案。
  • @jenniferruurs 看起来您正在尝试编译 JSON 文件。相反,编译程序并为其提供 JSON 文件。 gcc -O2 $(pkg-config --cflags glib-2.0 json-glib-1.0) -o test test.c $(pkg-config --libs glib-2.0 json-glib-1.0) 然后./test demo.jsonPassing in arguments is basic C。除非您这样做是为了学习 C,否则我建议您使用更简单的语言(如 Ruby 或 Python)来处理 JSON。
  • @jenniferruurs strdup is a POSIX function,在标准中并不严格,但很常见。 It's easy enough to write your own。因为无论如何我们都在使用 GLib,所以我们可以使用他们的 g_strdup 来代替。至于错误处理,您别无选择。如果您获得了无法解析的无效 JSON,则必须以某种方式处理。不客气。
猜你喜欢
  • 1970-01-01
  • 2011-04-08
  • 1970-01-01
  • 2021-07-14
  • 1970-01-01
  • 2018-01-24
  • 2021-12-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多