JSON简述
关于JSON的描述:
- JSON是存储和传输数据的格式。
- JSON经常在数据从服务器发送到网页时使用。
- JSON指的是 JavaScript Object Notation。
- JSON是轻量级的数据交换格式。
- JSON独立于语言。
- JSON是“自描述的”且易于理解。
当数据在浏览器与服务器之间进行交换时,这些数据只能是文本。JSON属于文本,并且我们能够把任何JavaScript对象转换为JSON,然后将JSON发送到服务器。我们也能把从服务器接收到的任何JSON转换为JavaScript对象。
JSON使用JavaScript语法,但是JSON格式是纯文本的。文本可被任何编程语言作为数据来读取和使用。
JSON与BIOS
目前EDK开源的代码中已经有对JSON的使用,它在RedfishPkg中体现:
PS D:\Gitee\edk2-beni\edk2> git submodule
-b64af41c3276f97f0e181920400ee056b9c88037 ArmPkg/Library/ArmSoftFloatLib/berkeley-softfloat-3
-f4153a09f87cbb9c826d8fc12c74642bb2d879ea BaseTools/Source/C/BrotliCompress/brotli
-d82e959e621a3d597f1e0d50ff8c2d8b96915fd7 CryptoPkg/Library/OpensslLib/openssl
-f4153a09f87cbb9c826d8fc12c74642bb2d879ea MdeModulePkg/Library/BrotliCustomDecompressLib/brotli
-abfc8ff81df4067f309032467785e06975678f0d MdeModulePkg/Universal/RegularExpressionDxe/oniguruma
-e9ebfa7e77a6bee77df44e096b100e7131044059 RedfishPkg/Library/JsonLib/jansson # RedfishPkg中包含的子模块
-1cc9cde3448cdd2e000886a26acf1caac2db7cf1 UnitTestFrameworkPkg/Library/CmockaLib/cmocka
-86add13493e5c881d7e4ba77fb91c1f57752b3a4 UnitTestFrameworkPkg/Library/GoogleTestLib/googletest
它是JSON的c语言实现,对应的开源库是https://github.com/akheron/jansson.git。
之所以会在RedfishPkg中,是因为Redfish传输数据的最通用方式就是JSON,为了在BIOS下实现Redfish(目前正式的EDK开源代码中还没有包含),就需要处理JSON。在RedfishPkg中提供了JSON的操作接口,位于RedfishPkg\Include\Library\JsonLib.h,由于涉及到的函数很多,这里就不全部列出。
BIOS下使用JSON
本节介绍在BIOS下使用JSON的示例,最终的代码可以在https://gitee.com/jiangwei0512/edk2-beni.git找到。
- 首先创建一个简单的JSON文件(example.json):
{
"name": "BENI",
"age": 18
}
我们会在Shell下操作它,所以需要将它放到FileSystem中,对应FS0:
- 然后在BIOS下包含JsonLib库:
JsonLib|RedfishPkg/Library/JsonLib/JsonLib.inf
该库还依赖于其它的一些库,比如Ucs2Utf8Lib、RedfishCrtLib等,也需要包含进去。
- 之后先一个Shell命令来测试该库,对应BeniPkg\DynamicCommand\TestDynamicCommand\TestDynamicCommand.inf。
这里需要注意一个问题,在JsonLib依赖的RedfishCrtLib会使用到另外的一个库BaseSortLib,而这个库Shell应用也需要用到(实际使用的是SortLib,但是两者对应的是一样的接口,但是实现不同),两者使用的是不同的代码实现,这导致了Shell命令需要使用的StringNoCaseCompare()
被ASSERT,导致使用异常,这里的解决方式是修改RedfishCrtLib的实现(对应RedfishPkg\PrivateLibrary\RedfishCrtLib\RedfishCrtLib.inf):
[LibraryClasses]
BaseLib
SortLib # 使用SortLib而不是BaseSortLib
DebugLib
MemoryAllocationLib
UefiRuntimeServicesTableLib
- 之后就是代码的具体实现。
这里首先获取文件example.json:
Status = ShellOpenFileByName (
FileName,
&FileHandle,
EFI_FILE_MODE_READ,
0
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "[%a][%d] Failed. - %r\n", __FUNCTION__, __LINE__, Status));
goto DONE;
}
Status = gEfiShellProtocol->GetFileSize (FileHandle, &FileSize);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "[%a][%d] Failed. - %r\n", __FUNCTION__, __LINE__, Status));
goto DONE;
}
Data = AllocateZeroPool (FileSize);
if (NULL == Data) {
DEBUG ((EFI_D_ERROR, "[%a][%d] Out of memory\n", __FUNCTION__, __LINE__));
goto DONE;
}
Status = ShellReadFile (FileHandle, &FileSize, Data);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "[%a][%d] Failed. - %r\n", __FUNCTION__, __LINE__, Status));
return;
}
这样就得到了JSON文件的数据。下一步是将数据转换成JSON,这需要使用JsonLib中的JsonLoadBuffer()
函数:
Json = JsonLoadBuffer (Data, FileSize, 0x4, &JsonError);
if (NULL == Json) {
DEBUG ((EFI_D_ERROR, "[%a][%d] Failed. - %r\n", __FUNCTION__, __LINE__, Status));
goto DONE;
}
Print (L"JsonValueIsObject: %d\r\n", JsonValueIsObject (Json));
Print (L"Json: %a\r\n", JsonDumpString (Json, EDKII_JSON_COMPACT));
测试得到的结果:
可以看到这里已经生成了BIOS下的JSON对象。如果想要得到其中具体的某一项:
JsonData = JsonObjectGetValue (Json, "name");
if (JsonValueIsString (JsonData)) {
Print (L"Name: %a\r\n", JsonValueGetAsciiString (JsonData));
}
这样就会打印name
键对应的值:
除了读取键值,还可以通过键修改对应的值,比如这里的name
:
// Create new JSON object for string.
NewData = JsonValueInitAsciiString ("JIANGWEI");
if (NULL == NewData) {
DEBUG ((EFI_D_ERROR, "[%a][%d] Failed. - %r\n", __FUNCTION__, __LINE__, Status));
goto DONE;
}
// Set JSON value.
Status = JsonObjectSetValue (Json, "name", NewData);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "[%a][%d] Failed. - %r\n", __FUNCTION__, __LINE__, Status));
goto DONE;
}
这里JSON对象就被修改了。如果想要将修改的值保存到文件,目前的JsonLib中似乎没有对应的函数,不过还是可以通过一些处理将数据写入文件:
FileSize = AsciiStrLen (NewJson);
FileData = AllocateZeroPool (FileSize + 2);
CopyMem (FileData, JsonDumpString (Json, EDKII_JSON_ENSURE_ASCII), FileSize);
FileData[FileSize] = 0xD;
FileData[FileSize + 1] = 0xA;
FileSize = FileSize + 2;
BeniDumpHex (2, 0, FileSize, FileData);
Status = ShellWriteFile (FileHandle, &FileSize, FileData);
这里是将字符串转换成了文件内容然后写入文件,区别是字符串以’\0’结尾,而文件最后是换行符(似乎不太对?)。