深入理解JSON与Struples

在现代编程中,JSON(JavaScript Object Notation)已经成为一种标准的代码数据交换格式。几乎所有的编程语言都以某种形式支持它,通常是以关联数组的形式出现,有时也会以模板和泛型的形式存在。JSON本身是一种交换格式,它非常适合表示嵌套的关联数组和列表中的简单数据,因此可以构建整个嵌套的“元组”结构,并将其表示为JSON。

JSON的主要用途是作为存储库和传输格式,但在这里更感兴趣的是其结构中的元组方面。JSON的主要限制在于它的简单性,它不支持很多数据类型。最明显的是,它缺少纯整数类型,也没有直接有效嵌入二进制数据的方法。此外,它也比需要的更冗长,例如,字段名称需要用引号包围。

显然,不是在这里重新发明一种新的传输格式,但只要能够在需要时输出正确的JSON,就没有什么坏处可以给JSON语法添加一些语法糖。因此,不需要字段名称周围的引号。可以扩展语法来隐式支持整数值,并且可以放宽对字符串转义和引用的一些限制。

对于其他任何事情,比如二进制字段,可以选择不将其渲染为JSON(仅限本地),或者可以简单地返回字段作为JSON兼容值的最佳努力近似。可能还想要解析片段——比如一个字段:“值”对,例如,所以不希望JSON的限制是文档必须由一个对象 { } 根。

重要的是——JSON数据应该能够立即与代码一起工作。Struples是JSON语法的超集——基本上是一个扩展的JSON语法,大约80%是语法糖,20%是没有JSON对应扩展的。

需要一种方式来读取和写入文本格式,以及一种方式来查询和操作结构,以便能够正确地使用。Struples使用.NET IDictionary和IList实现来存储内存中的树,结合一些扩展方法和DLR动态对象支持,使得查询这些内存中的树变得轻松,就像遍历C#中的任何结构一样熟悉,这使得它成为Struples的有效文档对象模型。

有一个拉式解析器,StrupleTextReader,类似于XmlTextReader,用于高效地从流源读取元组。解析器包含一些补救查询方法,用于通过字段选择一个项目,以及基本的读取、跳过和解析方法。

任何返回的Struple都可以转换为字符串,或者写入流或追加到StringBuilder,因此无论在什么情况下,写入都和解析一样高效。如果不想,永远不必处理整个文档加载到内存中,但可以。因此,只要适当使用,这段代码应该能够很好地扩展。

说到性能,这里有一个重要的考虑——格式良好性。Struple解析器以速度和批量数据处理为交易。因为几乎所有的JSON数据都是机器生成的,可以做出一个善意的假设,即数据是格式良好的。具体来说,性能的好处是值得风险的。Struples代码在解析和查询的许多阶段都假设数据的格式良好性。结果是提高了性能,缺点是在面对无效的JSON数据时的脆弱性。这几乎总是值得的。

使用代码的简要示例:

// 声明一个简单的员工对象,包含嵌入的二进制数据 string struple = @" {id: 1,firstname: 'Honey',lastname: 'Crisis', photo: ^iVBORw0KGgoAAAANS... } "; // 解析为DOM。由于Parse可以返回文档片段,它返回对象。 // 它可能返回KeyValuePair<string,object>在字段片段的情况下 // 一个IList<object>在“数组”的情况下 // 或者是一个int, double, bigint, long, boolean或null(object)或字符串 // 任何 { } 元组“对象”将返回一个Struple (IDictionary<string,object>) var doc = Struple.Parse(struple) as Struple; // 通过索引器读取字段 var photo = doc["photo"] as byte[]; var id = (int)doc["id"]; var name = string.Concat(doc["firstname"]," ", doc["lastname"]); // 使用动态调用站点读取 dynamic employee = doc; photo = employee.photo; id = employee.id; name = employee.firstname + " " + employee.lastname; // 演示设置一些字段(也可以使用DOM索引器完成) employee.lastname = "The Monster"; employee.photo = null; // 清除这个,以免淹没控制台 // 将结果以JSON形式漂亮地打印到控制台 StrupleUtility.WriteJsonTo(employee, Console.Out, ""); Console.WriteLine(); // 以本地形式漂亮地打印Struple StrupleUtility.WriteTo(employee, Console.Out, ""); Console.WriteLine(); // 从流中解析——可以是URL // Struple.ReadFrom()更容易——这只是长手 using (var tr = new StrupleTextReader(new StreamReader(@" ..\..\..\Burn Notice.2919.tv.json"))) { // 解析为DOM doc = tr.ParseSubtree() as Struple; // 使用StrupleUtility.Format写出一些数据 Console.WriteLine("Using StrupleUtility.Format"); Console.WriteLine(StrupleUtility.Format("Show: {name} ({homepage}), Last episode name: {last_episode_to_air.name}", (Struple)doc)); // 使用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)); // 使用Get扩展方法写出一些数据 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"))); // 与StrupleTextReader做一些性能测试 Console.WriteLine(); ReadSkipParsePerf(); }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485