快速且有效的JSON对象模型,并支持以符合JSON的格式有效解析和写作。
该库仅取决于NESLIB存储库。它作为该存储库的子模型包含。
该库的主要切入点是IJsonDocument界面。它用于解析,加载和保存JSON文档,并提供对JSON对象模型的访问权限。您可以如下解析JSON字符串:
var
Doc: IJsonDocument;
begin
Doc := TJsonDocument.Parse( ' { "Answer" : 42 } ' );
end ;请注意,与官方的JSON规范不同,该库不需要围绕字典键的引号(只要密钥不包含空格或其他非识别符字符)。因此,以下情况也有效:
Doc := TJsonDocument.Parse( ' { Answer : 42 } ' );您也可以使用Load方法从文件或流中加载。
在输出侧,您可以使用Save将其保存到文件或流中,或者将ToJson保存到JSON字符串中。
您还可以使用CreateArray或CreateDictionary方法从头开始创建新的JSON文档:
var
Doc: IJsonDocument;
begin
Doc := TJsonDocument.CreateArray;
Doc.Root.Add( 42 );
end ;如您在此示例中所见,您可以通过根属性访问JSON文档对象模型。
JSON对象模型的核心是TJsonValue类型。这是可以保存任何类型的JSON值的记录。
它提供了各种隐式转换运算符,以将TJsonValue转换为另一种(Delphi)类型。此外,还有To*方法可以尝试转换TJsonValue但如果转换失败,则返回提供的默认值。
您(可以)永远不会创建TJsonValue的自己;创建TJsonValue的唯一方法是将值添加到JSON数组或字典中:
var
Doc: IJsonDocument;
begin
Doc := TJsonDocument.CreateArray;
Doc.Root.Add( 42 );
end ;此示例将TJsonValue (具有值42)添加到JSON数组中。要创建一个新的字典数组,您可以使用AddArray或AddDictionary方法:
var
Doc: IJsonDocument;
Dict: TJsonValue;
begin
Doc := TJsonDocument.CreateArray;
Dict := Doc.Root.AddDictionary;
Dict.AddOrSetValue( ' answer ' , 42 );
end ;这会创建一个新的词典并将其添加到根数组中。然后,以“答案”的名称添加了该值42。
要检查值的类型,请使用TJsonValue.ValueType属性或TJsonValue.Is*方法之一。
当尝试在不是数组(或字典)的值上使用Add (或AddOrSetValue )之类的方法时,将提高异常。
但是,即使数组索引不超出界限,访问数组中的项目(使用Items属性)或字典中的值(使用Values属性)也不会导致例外。这允许将多个数组/字典访问链接在一起,而无需检查每个中间步骤的有效性。例如:
I := Doc.Root.Items[ 3 ].Values[ ' foo ' ].Values[ ' bar ' ].Items[ 4 ].ToInteger( 0 );这将始终成功,但是如果任何中间值不可用,则返回0。
IJsonDocument界面使读取和写入JSON变得容易到文档对象模型。
但是,如果您愿意,您也可以选择手动读取JSON(例如,避免将对象模型加载到内存中)。您可以使用Neslib.Json.IO单元中的IJsonReader和IJsonWriter接口来执行此操作。
这些接口完全独立于任何DOM实现,甚至不需要Neslib.Json单元。使用这些接口更为复杂,但是需要更多的工作。有关更多信息,请参见Neslib.Json.IO单元。
您还可以使用类似XPath的JSONPATH实现来查询JSON文档。
没有官方的JSONPATH规范,但使用最广泛的版本似乎是Stefan Goessner开发的版本。
JSONPATH看起来像:
$.store.book[0].title或者
$['store']['book'][0]['title']两种表示都相同:您可以使用dot( . )或括号( [] )表示法来表示字典的孩子。支架也可以与数值索引一起使用,以表示索引阵列的孩子。
JSONPATH仅在括号内使用单个引号(')。我们还允许双引号(“),因为这些引号易于在Delphi字符串中使用。
简而言之:
$表示根的开头,其次是零或更多的子操作员( .或[] )。 $本身与整个文档匹配。*或'*' )通配符,以匹配所有孩子。例如, $.store.book[*].author匹配商店中所有书籍的作者。. )外,可以使用双点( .. )来搜索任何后代而不是直接的孩子。例如, $..author与所有作者匹配,而不论深度如何。这称为递归下降。$.store.book[0,2,3]匹配第一本书,第三和第四本书。[Start:End:Step]与儿童的切片(范围)匹配。这Start使用给定的Step尺寸(通常为1) End 。所有都是可选的,但必须给出至少一个值(和结肠):Start ,则暗示为0。负值表示从数组末端的偏移。End ,则切片将通过数组的末端提取。负值表示并从数组末端偏移。Step ,则暗示为1。List[2:]匹配第三个和所有元素。List[-2:]匹配最后两个元素。List[:2]匹配前两个元素。List[:-2]匹配最后两个元素。List[2:-2]匹配所有元素,但前两个和最后两个元素。List[-4:-2]从末尾匹配第三元素和第4个元素。List[::2]与均匀索引匹配所有元素。JSONPATH还具有 @运算符,可以允许自定义脚本表达式。我们不支持此操作员。
示例文档:
{ "store" : {
"book" : [
{ "category" : " reference " ,
"author" : " Nigel Rees " ,
"title" : " Sayings of the Century " ,
"price" : 8.95
},
{ "category" : " fiction " ,
"author" : " J. R. R. Tolkien " ,
"title" : " The Lord of the Rings " ,
"isbn" : " 0-395-19395-8 " ,
"price" : 22.99
}
],
"bicycle" : {
"color" : " red " ,
"price" : 19.95
}
}
}示例路径:
| 表达 | 结果 |
|---|---|
$ | 匹配根文档(单个值) |
$..* | 匹配文档中的所有成员(很多值) |
$.store.book[*].author | 商店中所有书籍的作者 |
$..author | 所有作者 |
$.store.* | 商店中的所有东西(2本书和一本自行车) |
$.store..price | 商店里面所有东西的价格 |
$..book[2] | 第三本书 |
$..book[-1:] | 最后一本书 |
$..book[:2] | 前两本书 |
JSONPATH API简单而简单。它由只有几种方法的TJsonPath记录组成。
对于一次性匹配,请使用静态Match方法:
var
Doc: IJsonDocument;
Matches: TArray<TJsonValue>;
begin
Doc := TJsonDocument.Load(...);
Matches := TJsonPath.Match(Doc, ' $.store.book[*].author ' );
end ;如果您打算在多个(子)文档上使用相同的路径,那么解析该路径的速度更快,然后多次应用它:
var
Doc1, Doc2: IJsonDocument;
Path: TJsonPath;
Matches1, Matches2: TArray<TJsonValue>;
begin
Doc1 := TJsonDocument.Load(...);
Doc2 := TJsonDocument.Load(...);
Path := TJsonPath.Create( ' $.store.book[*].author ' );
Matches1 := Path.Match(Doc1);
Matches2 := Path.Match(Doc2);
end ;您还可以在子树上运行路径:
var
Doc: IJsonDocument;
Store: TJsonValue;
Matches: TArray<TJsonValue>;
begin
Doc := TJsonDocument.Load(...);
Store := Doc.Root.Values[ ' store ' ];
Matches := TJsonPath.Match(Store, ' $.book[*].author ' );
end ;如果您仅对单个(或第一个)比赛感兴趣,则可以使用MatchSingle :
var
Doc: IJsonDocument;
Match: TJsonValue;
begin
Doc := TJsonDocument.Load(...);
if (TJsonPath.MatchSingle(Store, ' $.book[*] ' , Match)) then
...
end ;此JSON库中的所有内存管理都是自动的。 IJsonDocument界面拥有所有TJsonValue的界面,并在销毁文档时将其销毁(不范围)。
您唯一需要注意的是,在破坏文档后,您不应再使用任何TJSONVALUE记录了。这样做会导致不确定的行为,并可能崩溃。
您可以使用以下条件定义自定义某些行为:
JSON_UTF8 :使用UTF8String而不是无处不在的String 。所有字符串将被视为8位UTF-8字符串,而不是16位Unicode字符串。这样可以减少内存消耗并加快解析。但是,这意味着您还必须将此JSON库与UTF8Strings一起使用,否则Delphi将隐式转换Unicode字符串和UTF8Strings,这可能会损害性能。JSON_STRING_INTERNING :启用字典键的字符串Indioning。如果使用相同的键多次使用相同的键(当JSON从数据库导出时很常见),这会减少内存的消耗,但要慢一些。 NESLIB.JSON单元根据JSON_UTF8定义,将JsonString类型声明为String或UTF8String 。但是,这并不意味着您也必须使用JsonString 。如果您不在乎JSON_UTF8定义,则可以在此库中使用常规字符串。
Neslib.JSON已根据简化的BSD许可获得许可。
有关详细信息,请参见License.txt。