2 个不稳定版本

0.2.0 2021年9月19日
0.1.0 2021年9月12日

#2027 in 解析器实现

MIT 许可证

180KB
4.5K SLoC

sgmlish

Build status Version badge Docs badge

sgmlish 是一个用于解析、操作和反序列化 SGML 的库。

它不是完整的 SGML 规范实现;特别是,DTD 不受支持。这意味着在解析之前必须配置大小写规范化和实体,并且任何所需的验证或规范化,如插入省略的标签,必须通过内置转换或手动实现。

尽管如此,其支持足够完整,可以成功解析 SGML 文档以用于常见应用,如 OFX 1.x,并且只需进行少量额外工作即可准备好委托给 Serde

非目标

  • 解析 HTML。 尽管 HTML 4 规范被定义为 SGML DTD,但那个时代的浏览器从未完全符合 SGML 的所有复杂性,并且网站几乎不考虑这一点。

    尝试使用 SGML 解析器来理解现实世界的 HTML 是一场必败的战斗;因此,HTML5 规范 考虑到了这一点,描述了如何以最佳方式处理网页可能出现的所有错误。

    如果您需要解析 HTML,即使是旧 HTML,请务必使用类似 html5ever 的工具。

  • 解析 XML。 这个空间由现有的库如 xml-rs 充分利用。 serde-xml-rs 提供了与这个库非常相似的解序列化体验。

  • 以下 SGML 特征在没有解析期间完全的 doctype 意识下难以正确实现,因此目前被认为超出了这个库的范围

    • NET (空结束标签) 形式: <FOO/example/
    • 字符集的自定义定义,如 SEPCHARLCNMSTRT

使用方法

这是使用 Serde 推导数据结构反序列化的快速指南。

首先,将 sgmlishserde 添加到您的依赖中

# Cargo.toml
[dependencies]
serde = { version = "1.0", features = ["derive"] }
sgmlish = "0.2"

定义您的数据结构类似于使用任何其他 Serde 库

use serde::Deserialize;

#[derive(Deserialize)]
struct Example {
  name: String,
  version: Option<String>,
}

使用通常分为三个步骤

let input = r##"
    <CRATE>
        <NAME>sgmlish
        <VERSION>0.2
    </CRATE>
"##;
// Step 1: configure parser, then parse string
let sgml = sgmlish::Parser::build()
    .lowercase_names()
    .parse(input)?;
// Step 2: normalization/validation
let sgml = sgmlish::transforms::normalize_end_tags(sgml)?;
// Step 3: deserialize into the desired type
let example = sgmlish::from_fragment::<Example>(sgml)?;
  1. 解析:根据需要配置sgmlish::Parser —— 例如,通过标准化标签名称或定义如何解析实体(&example;)来解决问题。一旦配置完成,就可以将其SGML字符串输入。

  2. 标准化/验证:由于解析器不了解DTD,它不知道如何在您的用例中插入隐含的结束标签,或者如何处理其他更神秘的SGML功能,例如空标签。 在继续反序列化之前必须修复此问题。

    此库提供了一个标准化转换函数:normalize_end_tags。它假定当元素不能包含子元素时才会省略结束标签。此算法对许多SGML应用(如OFX)来说已经足够好了。

  3. 反序列化:一旦事件流被标准化,就可以将其传递给Serde,让它施展魔法。

反序列化时的解释

  • 基本类型和字符串:值可以是容器元素的属性,或者是具有文本内容的子元素。

    以下与反序列化器等效

    <example foo="bar"></example>
    <example><foo>bar</foo></example>
    
  • 布尔值:字符串 truefalse10 都被接受,既可以作为属性值,也可以作为文本内容。

    在属性的情况下,也接受HTML风格的标志:空值(显式或隐式)以及等于属性名称的值(不区分大小写)被视为 true

    以下所有都设置 checkedtrue

    <example checked></example>
    <example checked=""></example>
    <example checked="1"></example>
    <example checked="checked"></example>
    <example checked="true"></example>
    <example><checked>true</checked></example>
    
  • 结构体:标签名称来自父结构体的字段,而不是来自值类型!

    #[derive(Deserialize)]
    struct Root {
      // Expects a <config> element, not <MyConfiguration>
      config: MyConfiguration,
    }
    

    如果您想捕获元素的文本内容,可以使用特殊名称 $value

    #[derive(Deserialize)]
    struct Example {
      foo: String,
      #[serde(rename = "$value")]
      content: String,
    }
    

    当使用 $value 时,所有其他字段必须来自容器元素的属性。

  • 序列:序列是从具有相同名称的连续元素序列中读取的。类似于结构体,标签名称来自 父结构体 的字段。

    #[derive(Deserialize)]
    struct Example {
      // Expects a series of <host> elements, not <Hostname>
      #[serde(rename = "host")]
      hosts: Vec<Hostname>,
    }
    

库功能

  • serde —— 包括对 Serde 反序列化的支持。

    由于这是此库的主要用例,因此默认启用此功能。要禁用它,请将 default-features = false 设置在您的 Cargo.toml 文件中。

依赖关系

~1.3–2.2MB
~44K SLoC