JSON 使用指南

本页介绍如何在 GameMaker 中使用 JSON。

内容

  1. JSON
  2. GameMaker 中的 JSON
    1. 使用哪些功能
    2. 数据转换
    3. 过滤功能
  3. 编写 JSON
    1. 美化
    2. 使用说明
  4. 读取 JSON
    1. 使用说明
  5. 序列化

JSON

JSON(JavaScript 对象表示法) 是一种轻量级数据交换格式,对于人和机器来说都易于读写。它建立在两个基本结构之上:

重要JSON 与数字 无关 。在任何编程语言中,都可以有各种不同容量和补数的数字类型,固定或浮动,二进制或十进制。这会使不同编程语言之间的互换变得困难。相反,JSON 只提供人类使用的数字表示形式:数字序列。所有编程语言都知道如何理解数字序列,即使它们在内部表示上存在分歧。如需了解更多信息,请参阅 ECMA JSON 标准

GameMaker 中的 JSON

GameMaker 通过两组函数内置支持读取和写入 JSON:

使用哪些功能

本指南重点介绍函数 json_parsejson_stringify

数据转换

JSON 标准支持的值数量有限:对象、数组、字符串、数字以及值 truefalsenullGameMaker 使用的 数据类型 比这更多,为了以稍后可以读回的方式存储这些数据,它将它们转换为字符串表示形式。json_parse/json_stringifyjson_decode/json_encode 都会执行这些转换。

注意 您可以通过将 json_parseinhibit_string_convert 参数设置为 true 来禁用解析 JSON 时的自动字符串转换。

下表列出了在 JSON 数据和 GameMaker 之间进行转换时发生的转换:

JSON 数据转换
JSON TypeGM Type/Value笔记
true / falsetrue / false 
real / numberReal 
"@i64@hex_value$i64@"int64GameMaker 会将其写为:
- 如果在 int32 的有效范围内,则为 int
- 如果可以在不丢失精度的情况下这样做,则为 double
- 一个 字符串 ,其前面带有标识符 "@i64@",后面带有标识符 "$i64$"( 如果这两种情况都不适用)。

当您再次读取 JSON,GameMaker 将获取这些标识符并将值转换回 int64( 除非禁用转换)。

警告 如果 JSON 用于服务器或某些其他非 GameMaker 目标,这些值不合适,因此应避免使用。
nullundefined
pointer_null
undefinedpointer_null 写入为 JSONnull解析解码 时,JSONnull 始终会读回为 undefined
stringString 
"@@infinity$$", @@-infinity$$infinity, -infinityinfinity 被写为字符串
"@@nan$$"NaNNaN 写为字符串
objectStruct (or DS Map) 
arrayArray (or DS List) 
"ref <type> <id>"Handle写入 JSON 时,句柄存储在其字符串表示形式 “ref <type> <id>” 中,读回 JSON 时则转换回运行时值。

使用 json_encode 时会出现例外情况: 您已标记的嵌套 DS MapsDS List 将分别被写入 JSON 对象和数组。

注意 依赖此行为通常没什么用,因为索引会在游戏运行之间发生变化。

过滤功能

如果您希望完全控制 JSON 内容的读取或写入方式,您可以选择提供一个针对每个元素运行的过滤器函数,以便 json_stringifyjson_parse。请参阅功能页面了解更多信息。

编写 JSON

编写 JSON 是通过 json_stringify 完成的。例如,以下结构体:

my_struct =
{
    test: "this",
    val: ["that", "another thing", {"value": "a nested struct"}]
};

可以使用 json_stringify 转换为 JSON 字符串:

struct_json = json_stringify(my_struct);

然后使用文本文件函数写入文件:

var _file = file_text_open_write("struct_as_json.json");
file_text_write_string(_file, struct_json);
file_text_close(_file);

或者缓冲函数:

var _buff = buffer_create(16384, buffer_fixed, 1);
buffer_write(buffer_text, struct_json);
buffer_save("struct_as_json.json");
buffer_delete(_buff);

您还可以通过网络发送 JSON 字符串或将其存储在其他位置 (例如数据库的文本字段中)。

以下文本是输出:

{"test":"this","val":["that","another thing",{"value":"a nested struct"}]}

默认情况下,json_stringify 写入 线性化 的 JSON。在这种情况下,所有内容都放在同一行上,中间没有空格,也没有添加缩进。当每个字节都很重要时,这种格式很有用,尽管它不是特别可读。

美化

prettify 参数可以设置为 true,以向生成的 JSON 字符串添加缩进,使其看起来 " 漂亮",即更易于人类阅读。

以下代码:

struct_json = json_stringify(my_struct, true);

outputs the following JSON: 

{
  "test":"this",
  "val":[
    "that",
    "another thing",
    {
      "value":"a nested struct"
    }
  ]
}

与线性化输出相比,上面的 JSON 一目了然地显示了数据的构造方式。

使用说明

读取 JSON

要从之前创建的 JSON 字符串中读取数据,您可以使用 json_parse 将其转换为结构体:

my_struct = json_parse(struct_json);

默认情况下,将检查 JSON 中的字符串值以查看它们是否包含特殊值。如果是,这些值将转换为内部 GameMaker 值。例如:

json = json_stringify({ "infinity": infinity, "NaN": NaN});
show_debug_message(json);                                    // {"NaN":"@@nan$$","infinity":"@@infinity$$"}
data_with_strings_converted = json_parse(json);
show_debug_message(data_with_strings_converted);             // { NaN : NaN, infinity : inf }
data_with_strings_unchanged = json_parse(json, , true);
show_debug_message(data_with_strings_converted);             // { NaN : "@@nan$$", infinity : "@@infinity$$" }

使用说明

序列化

序列化 描述如下:

In computing, serialization (or serialisation) is the process of translating a data structure or object state into a format that can be stored (e.g. files in secondary storage devices, data buffers in primary storage devices) or transmitted (e.g. data streams over computer networks) and reconstructed later (possibly in a different computer environment).

GameMaker 中,结构体就是这样一种数据结构。它可以包含变量和函数作为其成员。当您使用 json_stringify 对结构体的内容进行字符串化时,仅写入结构体的变量,而不写入其函数。

注意 您只能在 GameMaker 中以这种方式序列化 结构 ,而不能序列化 数据结构实例

通过手动设置 json_parse 使用 static_set 返回的结构的 静态结构 ,您可以将这些结构恢复到其原始状态。

例如,假设您有一个构造函数 Vector

function Vector(_x=0, _y=0, _z=0) constructor
{
    x = _x;
    y = _y;
    z = _z;
    
    static add = function(_v2)
    {
        x += _v2.x;
        y += _v2.y;
        z += _v2.z;
        
        return self;
    }
    static sum = function(_v1, _v2)
    {
        // Note: call using Vector.sum(v1, v2)
        return new Vector(_v1.x + _v2.x, _v1.y + _v2.y, _v1.z + _v2.z);
    }
    static dot = function(_v1, _v2)
    {
        // Note: call using Vector.sum(v1, v2)
        return dot_product_3d(_v1.x, _v1.y, _v1.z, _v2.x, _v2.y, _v2.z);
    }
    // Other
}

然后,您创建其中一些向量:

v1 = new Vector();
v2 = new Vector(100, 100);
v3 = new Vector(200, 20, 50);
v1.add(v2);
v4 = Vector.sum(v2, v3);

这四个向量可以存储在数组中,并使用 json_stringify 转换为 JSON:

json = json_stringify([v1, v2, v3, v4]);

该 JSON 字符串可以保存或发送到其他地方。

此时,您可能希望在游戏的另一个实例中恢复这些向量,不仅仅是它们的值 (xyz),还有它们的行为 (add, sum, dot,...)。

要将信息返回到结构中,您可以使用 json_parse 解析 JSON 字符串,然后将各个数组元素分配给之前使用的变量名称:

vectors = json_parse(json);
v1 = vectors[0];
v2 = vectors[1];
v3 = vectors[2];
v4 = vectors[3];

现在,这些结构已从 JSON 字符串中读回,但它们是完全新的结构,不再属于构造函数。这些结构在游戏的原始实例中是 Vector 结构,但现在不再是了。至少,不是 " 官方 " 的,正如您在调用函数 instanceof 时看到的那样:

instanceof(v1, Vector);  // false
instanceof(v2, Vector);  // false
instanceof(v3, Vector);  // false
instanceof(v4, Vector);  // false

要告诉 GameMaker 这些结构中的每一个都应再次属于 Vector 构造函数,您可以使用函数 static_set

var _static_vec = static_get(Vector);
static_set(v1, _static_vec);
static_set(v2, _static_vec);
static_set(v3, _static_vec);
static_set(v4, _static_vec);

通过在结构上使用 static_set,您首先将它们作为简单的 " 数据 " 结构与静态分离,然后将它们作为 Vector 静态结构的 " 子 " 结构重新附加到静态链。由于您可以访问结构所属的静态结构 (或构造函数) 层次结构的所有静态成员,因此您可以通过这种方式为结构提供对 Vector静态链 的访问权限,从而恢复其行为。

从现在开始,v1v4 都是 Vector 的实例!

instanceof(v1, Vector);  // true!
instanceof(v2, Vector);  // true!
instanceof(v3, Vector);  // true!
instanceof(v4, Vector);  // true!

您可以像任何其他 Vector 结构一样使用它们:

v1.add(v2).add(v3);
v5 = Vector.sum(v1, v1);

重要 为了使其正常工作,函数必须定义为静态,以便它们属于 构造函数 ,而不是结构体 实例 。如果不将函数声明为静态,则每个结构体实例都应通过执行构造函数来接收该函数的副本。不过,使用 static_set 设置静态结构不会调用构造函数,而是将该结构移动到 静态链 中的不同位置,从而使其能够访问不同静态结构的函数 (请参阅 点运算符如何查找变量名称 )。