Struples:一个有用的 JSON 超集





5.00/5 (4投票s)
用于解析、查询和输出 JSON 或 JSON 超集的代码
请注意,这取代了 JsonUtility 项目。
引言
元组是现代代码中的一个重要组成部分,似乎每种语言都有某种形式的元组,通常以关联数组的形式,有时是模板和泛型。
JSON 本身是一种交换格式,它本身就适合在嵌套的关联数组和列表中表示简单数据,因此您可以构建整个嵌套的“元组”结构并将它们表示为 JSON。
JSON 还有许多其他用途,尤其是作为存储库和线格式,但我们在这里更感兴趣的是它的结构中的元组方面。
JSON 的主要限制是它非常简单,无法处理很多数据类型。最值得注意的是,它缺少一个纯整数,并且无法直接有效地嵌入二进制数据。它也比需要的更冗长,例如,需要用引号引起来表示字段名称。
显然,我们在这里不是要重新发明一种新的线格式,但只要我们可以在需要时输出正确的 JSON,在 JSON 语法中添加一些语法糖是没有坏处的。所以我们不需要用引号引起来表示字段名称。我们可以扩展我们的语法以隐式支持整数值,并且我们可以放宽对字符串转义和引用的某些限制。
对于其他任何东西,例如二进制字段,我们可以避免将其呈现为 JSON(纯粹是本地的),或者我们可以简单地将该字段的最佳近似值作为符合 JSON 的值返回。
此外,我们可能需要解析片段 - 例如,像 field: "value"
这样的对,所以我们不希望 JSON 限制文档被 object { }
根节点限制。
重要的是 - JSON 数据应该开箱即用。Struples 是 JSON 语法的超集 - 基本上是一个扩展的 JSON 语法,大约 80% 是语法糖,20% 是无 JSON 对应的扩展。
我们需要一种从文本格式读取和写入的方法,以及一种查询和操作结构以使其真正有用的方法。
Struples 使用 .NET IDictionary<string,object>
和 IList<object>
实现来存储内存中的树,这与一些扩展方法和 DLR 动态对象支持相结合,使查询这些内存中的树变得无痛,并且与遍历 C# 中的任何结构一样熟悉,并使这成为 struples 的有效文档对象模型。
有一个拉式解析器,StrupleTextReader
类似于 XmlTextReader
,用于从流源有效地读取元组。解析器包含一些基本的查询方法,用于按字段选择项目,以及基本的读取、跳过和解析方法。
任何返回的 struple 都可以转换为 string
,或者写入 stream
或追加到 string
构建器,因此写入的效率与解析一样,无论情况如何。
如果您不想这样做,您永远不必处理加载到内存中的整个文档,但您可以。因此,只要使用得当,此代码应该能够很好地扩展。
说到性能,这里有一个重要的考虑因素 - 格式良好性。
struple 解析器依赖于速度和批量数据处理。由于几乎所有 JSON 数据都是机器生成的,我们可以善意地假设数据是格式良好的。具体来说,性能优势值得承担风险。struples 代码在解析和查询的许多阶段都对数据的格式良好性做出了假设。结果是性能提高,缺点是面对无效 JSON 数据时会出现脆弱性。这几乎总是值得的。
Using the Code
简而言之,一个例子
// see the source for a full example
// declare a simple employee object with embedded binary data
// below is truncated in this code pane. See the full source
string struple = "{id: 1,firstname: 'Honey',lastname: 'Crisis', photo: ^iVBORw0KGgoAAAANS... }";
// parse it into a DOM. Since Parse can return document fragments, it returns object.
// it may return KeyValuePair<string,object> in the case of a field fragment
// an IList<object> in the case of an "array"
// or an int, double, bigint, long, boolean or null(object) or string
// a Struple (IDictionary<string,object>) will be returned for any { } tuple "object"
var doc = Struple.Parse(struple) as Struple;
// demonstrate reading the fields by indexer
var photo = doc["photo"] as byte[];
var id = (int)doc["id"];
var name = string.Concat(doc["firstname"], " ", doc["lastname"]);
// demonstrate reading by using a dynamic call site
dynamic employee = doc;
photo = employee.photo;
id = employee.id;
name = employee.firstname + " " + employee.lastname;
// demonstrate setting some of the fields (can be done using the DOM indexers as well)
employee.lastname = "The Monster";
employee.photo = null; // clear this so it doesn't flood the console
// pretty print the result to the console as JSON
StrupleUtility.WriteJsonTo(employee, Console.Out, " ");
Console.WriteLine();
// pretty print the struple in native form
StrupleUtility.WriteTo(employee, Console.Out, " ");
Console.WriteLine();
// demonstrate parsing from a stream - can be an URL
// Struple.ReadFrom() is easier - this is just longhand
using (var tr = new StrupleTextReader(
new StreamReader(@"..\..\..\Burn Notice.2919.tv.json")))
{
// parse into a dom
doc = tr.ParseSubtree() as Struple;
// write out some of the data using StrupleUtility.Format
Console.WriteLine("Using StrupleUtility.Format");
Console.WriteLine(
StrupleUtility.Format(
"Show: {name} ({homepage}), Last episode name: {last_episode_to_air.name}",
(Struple)doc)
);
// write out some of the data using string.Format/dynamic
dynamic show = doc;
Console.WriteLine("Using string.Format with dynamic");
Console.WriteLine(
string.Format(
"Show: {0} ({1}), Last episode name: {2}",
show.name,
show.homepage,
show.last_episode_to_air.name
)
);
// write out some of the data using the Get extension method
Console.WriteLine("Using string.Format with Get");
Console.WriteLine(
string.Format(
"Show: {0} ({1}), Last episode name: {2}",
doc.Get("name"),
doc.Get("homepage"),
doc.Get("last_episode_to_air").Get("name")
)
);
// do some perf stuff with the StrupleTextReader
Console.WriteLine();
ReadSkipParsePerf();
}
StrupleTextReader
的查询功能非常有限,因此可以安全地认为未完成。但是,现有的功能是有效的。
历史
- 2019 年 4 月 1 日 - 初始发布