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

改进 XML/HTML 语法的提案

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.94/5 (25投票s)

2021年3月9日

GPL3

23分钟阅读

viewsIcon

35277

全新改进的 XML/HTML 语法

目录

引言

XML 是一项伟大的技术,拥有许多有用、标准化且受良好支持的扩展,如 XML Schema、XPath、XQuery、XSLT 等。然而,XML 因其“过于冗长”而闻名。如果我们看看官方 W3C XML 推荐标准中阐述的设计目标之一,这并不令人惊讶:

“XML 标记的简洁性是最低程度的重要性。”

—— XML 规范

一个有趣的问题出现了:“我们能否保留技术,只改进语法,使 XML 和 HTML 更容易被人类阅读和编写?”

如本文所示,这个问题的答案是响亮的“是的,我们可以!”。

我们将探讨一种新语法的建议,它不那么冗长,易于阅读和编写,并且适用于各种 XML 文档,包括 HTML 代码。

注意

本文的读者应具备 XML、HTML 和 JSON 的基本知识。

现有替代方案

在尝试创造任何新事物之前,我们当然应该首先深入研究已有的内容。

本章回答了一个问题:是否存在一种比 XML/HTML 更人性化的标记语言,但同样适用于大型、复杂和不断变化的文档?

JSON

近年来,JSON 在流行度方面已超越 XML。

为了理解原因(在“语法”的上下文中),让我们看看 JSON 中一个简单的数据结构:

{
    "person": {
        "name": "Albert",
        "married": true,
        "address": {
            "street": "Kramgasse",
            "city": "Bern"
        },
        "phones": [ "123", "456"]
    }
}    

在 XML 中,代码可能看起来像这样:

<?xml version="1.0" encoding="UTF-8"?>
<person>
    <name>Albert</name>
    <married>true</married>
    <address>
        <street>Kramgasse</street>
        <city>Bern</city>
    </address>
    <phones>
        <phone>123</phone>
        <phone>456</phone>
    </phones>
</person>

许多人更喜欢 JSON 语法。它比 XML 版本更容易阅读且不那么冗长。不计算缩进空格,上述 JSON 代码需要 144 个字符来输入。XML 代码有 276 个字符。这增加了 92%!

上述例子引出了一个有趣的问题:

  • “我们不能干脆停止使用 XML,而是用 JSON 来处理所有事情吗?”

例如,我们能否使用 JSON 语法来编写 HTML 文档?

让我们试试。

这是一个简单的 HTML 片段:

<p>foo bar</p>

在 JSON 中,我们可以这样表达:

{ "p": "foo bar" }

让我们把 foo 写成斜体,把 bar 写成粗体

HTML

<p><i>foo</i> <b>bar</b></p>

JSON

{ "p": [ { "i":"foo" }, " ", { "b": "bar" } ] }

现在我们想把所有东西都显示为红色:

HTML

<p style="color:red;"><i>foo</i> <b>bar</b></p>

JSON

{ "p": { "style": "color:red;", "content": [ { "i": "foo" }, " ", { "b": "bar" } ] } }

我们可以美化代码,使其更易于阅读:

{
    "p":{
        "style":"color:red;",
        "content":[
            {
                "i":"foo"
            },
            " ",
            {
                "b":"bar"
            }
        ]
    }
}    

但现在,这行 HTML 代码已经变成了“一个包含大量水平和垂直空白的 14 行怪物”。

这并不是我们想要的。

除了 JSON 代码的复杂性迅速增加这一显而易见的事实外,还有另一个令人担忧的观察:

  • 在第一个例子中,p 元素的值是一个字符串:"p": "..."
  • 在第二个例子中,值变成了一个 JSON 数组:"p": [...]
  • 在最后一个例子中,它变异成了一个 JSON 对象:"p": {...}

这种变化很容易导致维护噩梦。检查数据结构的代码每次更改时都必须更新。例如,如果我们想提取元素 p 的文本,我们需要为这三种情况编写不同的代码。

XML 没有这个问题。p 的内容始终是子元素列表。

至此,您希望同意我们可以停止进一步的调查并继续前进。JSON 语法不适合以人类友好的方式描述像 HTML 文档这样的标记代码。这当然不意味着“JSON 不好”。在许多情况下,JSON 是一个不错的选择。它是 JavaScript 的原生部分,在大多数编程语言中都得到了很好的支持,并且有大量的 JSON 库和工具可用。然而,在我们寻找更好的标记语法的背景下,JSON(以及它的所有变体)不是一个选择。稍后,我们将看一个更完整的 HTML 示例,它证实了我们的结论。

YAML

减少冗长的一种方法是使用缩进来定义结构。YAML 可能是使用这种技术的最流行的语言。

这是我们之前看到的 JSON 示例的重印:

{
    "person": {
        "name": "Albert",
        "married": true,
        "address": {
            "street": "Kramgasse",
            "city": "Bern"
        },
        "phones": [ "123", "456"]
    }
}    

在 YAML 中,它变成了:

person:
    name: Albert
    married: true
    address:
        street: Kramgasse
        city: Bern
    phones:
        - 123
        - 456

好!

易于阅读和编写。

乍一看,我们似乎可以对所有类型的数据结构(包括标记代码)使用这种无噪音的语法。

事实证明,那将是一个非常糟糕的主意。YAML 和所有其他使用缩进来定义结构的语言的问题在于:它适用于小型、简单的结构(例如配置文件)。但是,如果我们需要管理具有深度嵌套结构的大型文档,那么它很快就会变得容易出错且难以维护。

此外,虽然使用缩进来定义结构有效地减少了冗长,但对于某些类型的文档,它也会导致更多的代码行。原因是每个子元素“必须”写在新行上。

为了说明这一点,让我们看看前一章中使用的简单 HTML 一行代码在 YAML 中将如何编写。这是 HTML 的重印:

<p style="color:red;"><i>foo</i> <b>bar</b></p>

在 YAML 中,代码将如下所示:

p:
    style: 'color:red;'
    content:
        - i: foo
        - ' '
        - b: bar

还有其他反对对空白敏感的文档的论据,例如混合空格和制表符的问题,以及无法在具有不同缩进级别的不同文档之间共享的代码片段。这些不便众所周知——这里无需重复。

最后,对空白敏感的方法迫使我们根据规则使用空白(规则可能变得非常复杂)。它剥夺了我们使用空白使文档更具吸引力和可理解性的自由。

至于 JSON,这并不意味着“YAML 不好”。YAML 在某些情况下非常适用。我想说的是,在 HTML 这样的标记语言中使用空白敏感性的想法注定要失败。根据 维基百科,YAML 首字母缩略词的含义从“Yet Another Markup Language”(另一种标记语言)改为“YAML Ain't Markup Language”(YAML 不是标记语言),这是可以理解的。

XML/HTML 忽略空白,这是一个正确的选择。

其他

存在大量其他标记语言,但我不知道有任何语法能够很好地替代 XML 语法。如果您知道一个好的替代方案,请留言。

还有许多工具和编辑器插件旨在减轻手动编写 XML 代码的痛苦。然而,本文的要旨不是“减轻”痛苦。我们想“消除”它。

新语法

在本章中,我将为 XML/HTML 文档提出一种新的替代语法。新语法应适用于人类和机器。因此,我们称之为“实用 XML”,或简称为 **pXML**。

元素

简单元素

让我们从以下 HTML 片段开始——一个只包含文本的简单元素:

<i>foo</i>

我们首先要做的是摆脱闭合标签语法 (</i>)——这是 XML 冗长性最大的罪魁祸首。我们可以通过简单地用 > 闭合元素来做到这一点,就像这样:

<i>foo>

这会造成 <> 符号的不平衡。但这很容易解决。我们将开始标签中的 > 替换为空格——这是任何键盘上最容易阅读和写入的字符。代码变为:

<i foo>

让我们考虑一下括号。我们可以使用 <>,就像在 XML/HTML 中一样。但还有其他选项:[]{}()。我们需要考虑两点:

  • 它们容易输入吗?

    [] 显然获胜,因为在大多数键盘(包括 Dvorak 键盘)上,所有其他括号都需要按住 Shift 键。

  • 它们在普通文本中出现的频率如何?

    这很重要,因为括号在普通文本中必须进行转义。我没有找到任何可靠的统计数据,但我猜测 () 经常使用,而其他括号很少使用,可能按这个顺序:{}[]<>

最佳选择是使用 [],因为这对括号易于书写(在大多数键盘上无需按 Shift 键),并且方括号在普通文本中很少出现。此外,它在新 pXML 语法 ([])、XML/HTML (<>) 和源代码(通常使用 {})之间创建了清晰的区别。

因此,最终的 pXML 代码变为:

[i foo]

...比以下代码更容易阅读和编写:

<i>foo</i>

新语法的另一个优点是文本编辑器中(大多数现代版本都可用)的括号匹配变得更加有用。在 XML/HTML 的情况下,开始标签的 < 仅匹配“开始”标签的 >,这几乎没有用处。在 VSCode 中,它看起来像这样:

XML bracket matching in VSCode

在 pXML 中,开始标签的 [ 匹配“闭合”标签的 ],这更有帮助,尤其是在包含大量嵌套内容的元素的情况下。VSCode 示例:

pXML bracket matching in VSCode

空元素

空 XML 元素是没有属性和子节点的元素。

一个典型的例子是用于在 HTML 文档中插入新行的 br 元素。这是代码:

<br />  <!--     XML-compliant -->
<br>    <!-- not XML-compliant -->

在 pXML 中,这写为:

[br]

子元素

XML 元素可以选择包含一个或多个子元素。它们嵌入在开始标签和结束标签之间。这是一个例子:

<table>
    <tr><td>Cell 1.1</td><td>Cell 1.2</td></tr>
    <tr><td>Cell 2.1</td><td>Cell 2.2</td></tr>
</table>

pXML 中没有理由改变这一点。上面的例子写成这样:

[table
    [tr [td Cell 1.1][td Cell 1.2]]
    [tr [td Cell 2.1][td Cell 2.2]]
]

闭合标签

当嵌套元素关闭时,我们经常看到这样的 HTML 代码:

</img></div></section>

如果使用缩进,它看起来像这样:

        </img>
    </div>
</section>

在 pXML 中,不带缩进的代码变为:

]]]

带缩进的:

        ]
    ]
]

虽然新语法显然提高了书写速度并减少了代码大小,但它也带来了两个不便:

  • 代码变得不那么容易理解,尤其是在大型嵌套元素的情况下。

  • 在缺少 ](即,我们忘记关闭一个元素)或多余的 ] 的情况下,解析器生成的错误消息可能不那么有用。例如,想象一个大型文档中间有多余的 ]。解析器只能在文档末尾检测到错误,并报告最后一行有多余的 ]

    然而,请注意,当元素缩进,并且如果开始 [ 和结束 ] 的缩进不同时,解析器发出警告,可以大大缓解这个问题。

为了消除这些不便,我们应该支持一种“替代的”、更详细的语法来关闭元素。

[/tag]tag] 这样明显的选项不起作用,因为它们会造成括号不平衡,或者它们需要针对特殊情况制定特殊规则。我最终选择了以下替代语法:

][/tag]

因此,上面关闭三个元素的代码可以选择这样编写:

][/img] ][/div] ][/section]

...或者这样:

        ][/img]
    ][/div]
][/section]

这比 XML 语法稍微冗长一些,因为每个结束标签多了一个字符。但它也更容易编写,因为不需要两次使用 Shift 键。实际上,冗长语法只用于关闭包含多层子元素的大标签。所以我认为替代的 pXML 结束标签语法是一个很好的折衷方案。你需要时它就在那里,并且运行良好(没有需要特殊规则的特殊情况)。

命名规则

出于兼容性原因,pXML 中元素名称的规则必须与 XML 中的相同。元素名称:

  • 区分大小写
  • 必须以字母或下划线开头
  • 不能以“xml”开头
  • 可以包含字母、数字、连字符、下划线和句点
  • 不能包含空格

转义规则

pXML 使用反斜杠 (\ ) 作为转义字符。

文本中必须始终转义三个字符:

字符 转义标记
[ \[
] \]
\ \\

以下代码展示了文本 Watch out for <, >, ", ', &, [, ], and \ characters 在 XML 和 pXML 中如何转义:

XML:  <note>Watch out for &lt;, &gt;, &quot;, &apos;, &, [, ], and \ characters</note>

pXML: [note Watch out for <, >, ", ', &, \[, \], and \\ characters]

任何 Unicode 字符都可以通过使用编程语言中常用的 \uhhhh 语法插入。例如,文本:

Hell\u006f

被解析为:

Hello

XML 实体目前在 pXML 中不受支持。

在 XML 中,"a &lt; 1" 被解析为 a < 1。在 pXML 中,它被解析为 "a &lt; 1"

然而,如果 pXML 文档转换为 XML 文档(或 XML 转换为 pXML),转换会自动应用正确的转义规则。例如,以下 pXML 文本:

\[ <

...将被转换为以下 XML 文本:

[ &lt;

...反之亦然。

属性

概述

除了元素,XML 还支持属性。这是一个带有属性的 XML 元素的示例:

<div id="unplug_warning" class="warning big-text">Unplug power cord before opening!</div>

在 pXML 中,相同的代码看起来像这样:

[div ( id=unplug_warning class="warning big-text" ) Unplug power cord before opening!]

可以看出,这两种语法是相似的。有两个明显的区别:

  • 在 pXML 中,属性嵌入在括号中:(...)。语法类似于某些编程语言中的函数参数赋值。
  • 在 pXML 中,属性值不总是需要加引号(例如 id=unplug_warning)。

Names

在 XML 中,属性名称不加引号(与 JSON 不同),并且它们必须遵守我们之前看到的元素标签名称的相同规则。

为了与 XML 保持兼容,pXML 采用相同的规则。属性名称:

  • 必须以字母或下划线开头
  • 可以包含字母、数字、连字符、下划线和句点(无空格)

在 XML 中,属性值必须始终用 " 引号引起来。示例:name = "Bob"

在 pXML 中,值可以是无引号、双引号或单引号。

无引号值

如果值不包含以下内容,则“不需要”加引号:

  • 空白:<空格>、<制表符>、<回车>、<换行>
  • 方括号:[ 和 ]
  • 括号:( 和 )
  • 引号:" 和 '

示例

name = Bob
port = 8080
path = C:\Users\Alice

双引号和单引号值

如果值包含任何在无引号值中不允许的字符,则该值“必须”用双引号 (") 或单引号 (') 包裹。

示例

food = "healthy orange"                 // contains space
expression = "array[17]"                // contains square brackets
conclusion = "It's ok."                 // contains '
statement = 'He said: "All is well".'   // contains "

转义规则

无引号属性值不支持转义序列。因此,解析无引号值会更快一些。

双引号和单引号值支持转义。应用以下规则:

  • 反斜杠 (\ ) 用于开始转义序列。

  • 双引号值中的双引号必须用 \" 转义。示例:

    statement = "He said: \"All is well\"."    // He said: "All is well".
  • 单引号值中的单引号必须用 \' 转义。示例:

    conclusion = 'It\'s ok.'    // It's ok.
  • 反斜杠必须用 \\ 转义。示例:

    path = "C:\\Users\\Alice"    // C:\Users\Alice
  • 以下值可以“选择性地”进行转义:

    名称 语法
    方括号 \[ \]
    制表符 \t
    回车 \r
    换行 \n
  • 任何 Unicode 字符都可以通过使用编程语言中常用的 \uhhhh 语法插入。例如:

    word = "Hell\u006f"    // Hello

换行符

为了更好的可读性,属性赋值可以写在单独的行上,像这样:

[image (
    source = images/kid.png
    title  = "Kid is flabbergasted"
    width  = 800px
    height = 600px
)]

属性赋值之间的空白被忽略。

带引号的值可以包含换行符。示例:

statement = 'He said:       // He said:
"All is well!"'             // "All is well!" 

如果代码中像上面例子一样直接插入换行符,那么解析的实际换行符字符取决于操作系统以及用于创建代码的编辑器/程序配置。默认情况下:

  • 在 Unix/Linux 上,换行符是一个单独的 <换行> 字符
  • 在 Windows 上,换行符由两个字符组成:一个 <回车> 和 <换行>

在这两个系统上,此默认行为可能会被覆盖。例如,可以将 Windows 编辑器配置为为代码中插入的换行符生成单个 <换行> 字符。

为了在 pXML 代码中强制使用特定的换行符,我们可以在代码中使用转义序列,例如:

statement = 'He said:\r\n"All is well!"'    // Windows new line forced
statement = 'He said:\n"All is well!"'      // Unix new line forced

XML 和 pXML 之间的差异

在 pXML 中,写入:

[e (a1=v1 a2=v2)]

在语义上等同于:

[e [a1 v1][a2 v2]]

在这两种情况下,节点 e 都是一个包含两个子节点 a1a2 的节点。在这两种情况下,访问 e 内容的 API 是相同的。

pXML 属性只提供了一种替代的子节点“语法”。

替代语法很有用,因为它更适合于只包含文本的子节点集。

这是一个不使用属性的节点:

[image [source ball.png][width 300px][height 200px]]

使用属性定义的相同节点如下所示:

[image (source=ball.png width=300px height=200px)]

第二个版本(使用属性)稍微短一些,更易于阅读和编写,并且对于习惯 XML 属性语法的人来说更熟悉。然而,这两个版本都被解析为相同的树结构。

这与 XML 不同,在 XML 中属性和元素是不同的。在 XML 中,用于访问属性的 API 与用于子元素的 API 不同。因此,读取 XML 结构的程序必须知道值是定义为属性还是元素。此外,如果最初定义为属性的值后来定义为元素(反之亦然),则读取该值的程序必须更新。在 pXML 中则没有必要。

注释

XML 注释看起来像这样:

<!-- text of comment -->

我们可以通过使用 [- 开始注释,使用 -] 结束注释来简化。这是 pXML 版本:

[- text of comment -]

[- 不会与名为 - 的元素开始混淆,因为根据规则,元素名称不能包含连字符 (-)。

“嵌套”注释在实践中经常有用。例如,注释包含注释的代码块是很常见的。因此 pXML 支持嵌套注释(与 XML 不同)。这是一个示例:

[- text of outer comment
    [- text of inner comment -]
-]

注意

将 pXML 转换为 XML 代码的应用程序必须小心不要转换内部注释,因为 XML 不支持嵌套注释。上面的注释不能转换为:

<!-- text of outer comment
    <!-- text of inner comment -->
-->

它应该转换为:

<!-- text of outer comment
    [- text of inner comment -]
-->

其他

本入门文章不涵盖以下 XML 语法构造:XML 实体、命名空间、CDATA 节和处理指令。它们可能是后续文章的主题。

历史

pXML 的前身

当我开始思考新语法时,我根本没有考虑创建更好的 XML/HTML 语法。我“想要”的是一种编写文章(发布在博客上)和书籍的新语法。最初我使用 Docbook,然后使用 Asciidoctor 编写文章。我还尝试了 Markdown,并研究了其他语法,如 RestructedText。长话短说:我对现有解决方案的一些不切实际之处感到沮丧,最终决定设计一种名为“实用标记语言 (PML)”的新语法。如果您想了解我创建 PML 的更多动机,您可以阅读我们需要一种新的文档标记语言——原因在此(2019 年 3 月发布)。(注意:对于仍在使用文字处理器的读者,我还写了文档标记语言与所见即所得编辑器的优势)

现在,我用 PML 编写所有文章(包括这篇)。为了发布它们,我创建了一个“PML 到 HTML 转换器”,它读取 PML 文件并创建 HTML 文件。您可以在这里查看本文的 PML 源代码,并在这里查看其原始版本(即“PML 到 HTML 转换器”生成的结果)。如果您想查看“PML 到 HTML 转换器”生成的 HTML 代码,请右键单击原始文章,然后点击“查看页面源代码”。该转换器生成缩进、干净、简单的 HTML 代码,就像手写的一样。“PML 到 HTML 转换器”根据 GPL2 开放源代码,并用 PPL(“实用编程语言”)编写。源代码在 Github 上。

创建 PML 后,我突然意识到它的语法也可以用于编写 XML/HTML 文档——一个不错的副作用。本文是我公开我的想法的第一步。

可以说,PML 之于 pXML 就像 HTML 之于 XML。PML 使用 pXML 语法,但只允许使用预定义的语义 PML 标签。

PML 中的宽松语法

PML 的一个重要方面是解析器在“宽松模式”下工作的能力。这种模式支持非常有针对性的语法简化,旨在消除尽可能多的“噪音”。使用 PML 编写文章和书籍应该“很容易”。以下是一个例子,说明“宽松语法”的优势:

这是一个用严格 pXML 编写的简单 PML 文档的代码:

[doc (title=Test)
    [ch (title="An Unusual Surprise")
        [p Look at the following picture:]
        [image (source=images/strawberries.jpg)]
        [p Text of paragraph 2]
        [p Text of paragraph 3]
    ]
]

在宽松 PML 模式下(始终激活),文本可以缩短为:

[doc Test
    [ch An Unusual Surprise
        Look at the following picture:
        [image images/strawberries.jpg]
        
        Text of paragraph 2

        Text of paragraph 3
    ]
]

让我们简单看看它是如何工作的:

  • doc (title=Test) 变为 doc Test

    某些元素(例如 doc)具有“默认属性”。对于该属性,只需指定值即可——无需编写 (name=value),我们只需编写 value

  • [p Text of paragraph 2] 变为 Text of paragraph 2

    不包含在元素中的自由文本会自动嵌入到 p(段落)元素中。

    由两个换行符分隔的文本会自动创建段落分隔符。

如果你想尝试上面的代码,可以这样操作:

  • 下载“PML 到 HTML 转换器”

  • 在任何目录中创建文件 example.pml,其中包含上面显示的 PML 代码(严格 pXML 版本将不起作用)。

  • 将图片复制到 resources/images/strawberries.jpg

  • 在文件 example.pml 所在目录中打开一个终端并输入:

    pmlc example.pml
  • 在浏览器中打开文件 output/example.html

    结果如下:

    HTML page created by PML

  • 右键单击文本,然后选择“查看页面源代码”,如果你想查看“PML 到 HTML 转换器”生成的 HTML 代码。

实现

在使用 PML 语法一段时间来创建真实文章(不仅仅是测试)之后,我对 pXML 语法也应该适用于 XML 文档充满信心。然而,为了消除疑虑,在发布本文之前,我需要一个 pXML 的“概念验证”。因此,我创建了一个解析器,它可以读取本文中介绍的 pXML 语法。该解析器用 Java 编写,不依赖任何库。我将对其进行开源。

目前已实现以下功能:

  • 将 pXML 转换为 XML(应用 pXML/XML 转义规则)

  • 将 XML 转换为 pXML(应用 pXML/XML 转义规则)

  • 将 pXML 文档读取到 org.w3c.dom.Document Java 对象中。

    这是最强大的功能。一旦我们有了 Java Document 对象,我们就可以将所有 XML 的相关规范应用于 pXML 文档。几个例子是:

    • 使用 XML Schema (W3C)、RELAX NG 或 Schematron 验证文档

    • 以编程方式遍历文档

    • 插入、修改和删除元素和属性,并将结果保存为新的 XML 或 pXML 文档

    • 使用 XQuery/XPath 查询文档(搜索值、计算聚合等)

    • 使用 XSL 转换器转换文档(例如,创建不同结构的 XML 文档、创建纯文本文档等)

这是一个 pXML 到 XML 转换的“Hello World”示例:

  • 假设我们创建了文件 hello.pxml,内容如下(一个名为 hello 的空根元素):

    [hello]
  • 以下 Java 代码将此 pXML 文件转换为名为 hello.xml 的 XML 文件:

    PXMLToXMLConverter.PXMLFileToXMLFile ( new File("hello.pxml"), new File("hello.xml") );
  • 生成的 hello.xml 文件如下所示:

    <?xml version="1.0" encoding="UTF-8"?>
    <hello />            

相反的操作(即,将 XML 文件转换为 pXML 文件)可以通过以下方式完成:

XMLToPXMLConverter.XMLFileToPXMLFile ( new File("hello.xml"), new File("hello.pxml") );

一旦 pXML 解析器准备好开源(计划于 2021 年 5 月),我将发布一篇包含更多示例的专门文章。

我还在开发一个专门的 pXML 网站,其中包含语法规范以及用 EBNF 和铁路图表示的语法。非常欢迎大家参与开源项目。

示例

一图胜千言。所以让我们看看两个常见的真实示例:一个简单的配置文件和 HTML 代码。我们将比较用 JSON、XML、pXML 和 PML 编写的代码。

简单配置文件

一个简单的配置文件只是一个(可能嵌套的)键/值对映射。

JSON

以下是 JSON 中的一个示例:

{
    "size":"XL",
    "colors":{
        "background":"black",
        "foreground":"light green"
    },
    "transparent":true
}

备注

需要引用名称和值有点烦人。

另一个不便是除了最后一个赋值外,每个赋值末尾都需要逗号。每次我们在列表末尾添加参数时,都有可能忘记在现有倒数第二行添加逗号。

XML

相同的配置数据在 XML 中看起来像这样:

<config>
    <size>XL</size>
    <colors>
        <background>black</background>
        <foreground>light green</foreground>
    </colors>
    <transparent>true</transparent>
</config>

备注:结束标签很嘈杂。

替代语法,使用属性:

<config>
    <size>XL</size>
    <colors background="black" foreground="light green" />
    <transparent>true</transparent>
</config>

备注:这两种语法不兼容 API。使用属性而不是元素需要更新访问 colors 值的代码。

pXML

pXML 版本看起来像这样:

[config
    [size XL]
    [colors
        [background black]
        [foreground light green]
    ]
    [transparent true]
]

替代语法,使用属性:

[config
    [size XL]
    [colors (background=black foreground="light green")]
    [transparent true]
]

备注:这两种语法兼容 API。使用属性而不是元素不需要更新访问 colors 值的代码。

冗长

为了比较三种语法的冗长性,让我们考虑一个参数所需的标记代码长度(不包括空白):

语言 标记 长度 Range 备注
JSON "":"", 6 3 到 6 对于整数、布尔值和 null 值,-2(因为它们没有加引号);对于最后一个参数,-1(因为它没有尾随逗号)
XML 元素 <></size> 9 最少 6 个 长度取决于名称中的字符数
XML 属性 ="" 3 始终为 3  
pXML 元素 [] 2 始终为 2  
pXML 属性 = 或 ="" 1 或 3 1 或 3 如果值不需要加引号,则长度为 1

结论

最冗长的语法是 XML(尤其是在参数名称较长的情况下)。最不冗长的是 pXML。更少的噪音意味着“易于人类阅读和编写”。

HTML 代码

现在我们来看看一些 HTML 代码——XML 最常见的用途。为了保持示例简短,我们只看一个 HTML 片段,省略 HTML 头部和底部。

HTML

以下代码代表一个包含三段和一个图片的章节:

<section>
    <h2>Harmonic States</h2>

    <p>The <i>initial</i> state looks like this:</p>
    <img src="images/state_1.png" />

    <p>After just a few <i><b>micro</b>seconds</i> the state changes.</p>
    
    <p>More text ...</p>
</section>

JSON

前一章我们已经看到 JSON 不适合编写类似 HTML 的代码。尽管如此,我们还是来看看我们的 HTML 片段的 JSON 版本——只是为了证实我们之前的结论:

{ "section": [
    { "h2": "Harmonic States" },

    { "p": [ "The ", { "i": "initial" }, " state looks like this:" ] },
    { "img": { "src": "images/state_1.png" } },

    { "p": [ "After just a few ", { "i": [ { "b": "micro" }, "seconds" ] }, 
      " the state changes." ] },
    { "p": "More text ..." }
] }

如果我们将代码美化,7 行代码将变成 38 (!) 行,其中空白比文本还多:

{
    "section":[
        {
            "h2":"Harmonic States"
        },
        {
            "p":[
                "The ",
                {
                    "i":"initial"
                },
                " state looks like this:"
            ]
        },
        {
            "img":{
                "src":"images/state_1.png"
            }
        },
        {
            "p":[
                "After just a few ",
                {
                    "i":[
                        {
                            "b":"micro"
                        },
                        "seconds"
                    ]
                },
                " the state changes."
            ]
        },
        {
            "p":"More text ..."
        }
    ]
}

谁会喜欢编写和维护这样的代码呢?然而这只是一个简单的玩具示例。想象一下一个包含真实世界、庞大而复杂的 HTML 代码的代码库!

pXML

这是 pXML 版本:

[section
    [h2 Harmonic States]

    [p The [i initial] state looks like this:]
    [img (src=images/state_1.png)]

    [p After just a few [i [b micro]seconds] the state changes.]
    
    [p More text ...]
]

PML

如前所述,PML 具有宽松语法模式,允许非常简洁的标记代码:

[ch Harmonic States

    The [i initial] state looks like this:
    [image images/state_1.png]

    After just a few [i [b micro]seconds] the state changes.
    
    More text ...
]

注意

如果我们将上述代码嵌入到 doc 元素中(如之前所示),将代码保存到文件 test.pml 中,并使用操作系统命令 pmlc test.pml 运行“PML 到 HTML 转换器”,就会创建一个完整的 HTML 文件(包含头部和尾部)。这是此文件的一个摘录(已移除 CSS 代码):

<section id="ch__1">
    <h2>Harmonic States</h2>
    <p>The <i>initial</i> state looks like this:</p>
    <figure>
        <img src="images/state_1.png" />
    </figure>
    <p>After just a few <i><b>micro</b>seconds</i> the state changes.</p>
    <p>More text ...</p>
</section>

可以看出,它与我们手写的原始 HTML 代码非常相似。

冗长

让我们看看数字。用这四种语言编写代码需要付出多少努力?如果我们提取标记代码(即,删除空白和浏览器中显示的文本),我们得到以下结果,从最差到最好:

JSON: {"section":[{"h2":""},{"p":["",{"i":""},""]},
      {"img":{"src":""}},{"p":["",{"i":[{"b":""},""]},""]},{"p":""}]}

HTML: <section><h2></h2><p><i></i></p><imgsrc=""/><p><i><b></b></i></p><p></p></section>

pXML: [section[h2][p[i]][img(src=)][p[i[b]]][p]]

PML:  [ch[i][image][i[b]]]

计算字符数,我们得到下表:

语言 标记长度 HTML 的百分比
JSON 108 132%
HTML 82 100%
pXML 42 51%
PML 20 24%

这些数字的图表如下所示:

Number of characters in JSON, HTML, pXML, and PML

当然,这并不是一个具有代表性的结果。其他 HTML 示例会导致或多或少不同的数字。然而,它清楚地显示了语法的影响。语法影响复杂性、空间和时间以及可用性。简洁的语法使代码更易于阅读和编写,也更令人愉悦。

语法比较

以下是 XML 与 pXML 语法的简要比较:

空元素

XML:  <br />
pXML: [br]

带有文本内容的元素

XML:  <summary>text</summary>
pXML: [summary text]

带子元素的元素

XML:  <ul>
          <li>
              <div>A <i>friendly</i> dog</div>
          </li>
      </ul>

pXML: [ul
          [li
              [div A [i friendly] dog]
          ]
      ]

属性

XML:  <div id="unplug_warning" class="warning big-text">Unplug power cord before opening!</div>
pXML: [div (id=unplug_warning class="warning big-text")Unplug power cord before opening!]

转义

XML:  <note>Watch out for &lt;, &gt;, &quot;, &apos;, &, [, ], and \ characters</note>
pXML: [note Watch out for <, >, ", ', &, \[, \], and \\ characters]    

注释

Single comment:
XML:  <!-- text -->
pXML: [- text -]

Nested comments:
XML:  not supported
pXML: [- text [- nested -] -]

总结与结论

正如所证明的,简化 XML 语法并使其对人类更易于访问“是”可能的。

本文介绍的 pXML 语法本质上提出了三项更改:

  • 替换 XML 语法:

    <name>value</name>

    ...为:

    [name value]
    From XML to pXML
  • 将属性嵌入括号之间,并尽可能允许不带引号的值。

    XML 代码:

    name1="value" name2="value with spaces"

    ...变为:

    (name1=value name2="value with spaces")
  • 支持嵌套注释(XML 中不支持)

虽然 pXML 语法不如 XML 冗长,并且与 XML 不同,但 XML 生态系统中的所有伟大补充仍然可以使用。一旦 pXML 文档被解析成 XML 树,文档就可以被验证、查询、修改和转换。

精心设计的语法可以提高生产力,减少错误,简化维护,并提高空间和时间效率。

语法很重要!

文章历史

  • 2021年3月10日
    • 第一版
  • 2021年4月20日
    • 添加了属性语法
    • 删除了可选的名称前缀 #(用于区分数据和元数据)
© . All rights reserved.