65.9K
CodeProject 正在变化。 阅读更多。
Home

Struples:一个有用的 JSON 超集

starIconstarIconstarIconstarIconstarIcon

5.00/5 (4投票s)

2019 年 4 月 2 日

CPOL

3分钟阅读

viewsIcon

10698

downloadIcon

111

用于解析、查询和输出 JSON 或 JSON 超集的代码

请注意,这取代了 JsonUtility 项目。

Struples

引言

元组是现代代码中的一个重要组成部分,似乎每种语言都有某种形式的元组,通常以关联数组的形式,有时是模板和泛型。

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 日 - 初始发布
© . All rights reserved.