测试已测试 | ✓ |
语言语言 | SwiftSwift |
许可证 | Apache 2 |
发布上次发布 | 2016年5月 |
SPM支持SPM | ✓ |
由Chris Sachs、Ewan Mellor、Chris Sachs维护。
RECON将属性带入对象符号时代,并为属性文本标记提供简单语法和统一的树型模型。RECON旨在结合JSON的最简主义与XML的表达力,以人性化的语法。
要开始使用RECON Swift库,把以下内容添加到您的CocoaPods配置文件中
use_frameworks!
pod 'Recon'
接下来运行pod install
来加载依赖。然后在您的代码中导入Recon
模块。
import Recon
RECON有三种原始数据类型:文本、数字和数据。
文本值有两种形式:一个引用的字符串或者一个未引用的标识符。
"string"
identifier
数字以十进制字面量序列化。
-1
3.14
6.02e23
二进制数据以一个前导的’%’符号开始,后跟一个base64字面量。
%AA==
RECON唯一的聚合数据类型,即《记录》,承载了数组和关联数组的作用。将记录视为一个部分键列表。下面的示例记录包含两个有序项,首先是“主题”字段,其值为“Greetings”,然后是未键入的字符串“Hello, Earthlings!”。
{ subject: "Greetings", "Hello, Earthlings!" }
一个逗号、一个分号或一个或多个换行符分隔项。换行分隔的记录为美观打印的文档提供了干净语法。
{
subject: "Re: Greetings"
"Hi Martians!"
}
记录支持任意值作为槽位键。
{
@planet Jupiter: {}
@god Jupiter: {}
}
顶层文档可以省略根记录周围的括号。我们将记录(不带大括号)的内容称为块。当块仅包含单个项时,块的价值简化为其包含项的值。下面的示例块等同于上面的示例记录。
subject: "Re: Greetings"
"Hi Martians!"
符号“@”用于引入属性。属性用于标识记录中的关键字段。前面的标记示例进一步简化为以下形式。
{
"Hello, "
{
"@em":
"world"
}
"!"
}
注意,上面所示的代码中@em
字段没有明确的值。RECON数据模型将未指定但实际上存在的值称为现存。我们说记录@em[world]
具有一个名为em
的现存属性。
当然,属性也可以有关联的值。在属性名称后面放置属性参数,并使用括号括起来。
@answer(42)
@event("onClick")
上面的属性在其结构上等同于以下内容:
{"@answer":42}
{"@event":"onClick"}
属性括号包围一个块,这意味着当需要时,属性值将构建一个隐式记录。以下是示例以及其简化后的等效表达式。
@img(src: "tesseract.png", width: 10, height: 10, depth: 10, time: -1)
{
"@img": {
src: "tesseract.png"
width: 10
height: 10
depth: 10
time: -1
}
}
属性修改邻近的值。修改后的值将插值到由其相邻属性形成的记录中。以下是具有前缀、后缀和环绕属性的值的示例。
@duration 30
30 @seconds
@duration 30 @seconds
@relative @duration 30 @seconds
上面的属性表达式简化为以下记录:
{ "@duration":, 30 }
{ 30, "@seconds": }
{ "@duration":, 30, "@seconds": }
{ "@relative":, "@duration":, 30, "@seconds": }
修改后的记录与它们的相邻属性形成的记录展开。因此,@point{x:0,y:0}
简化为{"@point":,x:0,y:0}
,而不是{"@point":,
{x:0,y:0}。
方括号表示标记。标记提供与记录相反的语法,值嵌入在文本中,而不是文本嵌入在记录中。
[Hello, @em[world]!]
实际上,标记只是记录的语法糖。上面的示例表达的结构与下面的结构完全相同。
{ "Hello, "; @em "world"; "!" }
标记内的花括号将包围的块提升到标记的记录中。以下记录等效。
[Answer: {42}.]
{ "Answer", 42, "." }
方括号将嵌套的标记提升到封装记录中。如果你想原样包含它们,请确保转义方括号。
[Say [what]?]
{ "Say ", "what", "?"}
[Say \[what\]?]
{ "Say [what]?" }
在同一标记内的顺序属性不会链式连接;每个标记内嵌的属性插入一个嵌套记录。
[http@colon@slash@slash]
{ "http", @colon, @slash, @slash }
标记中的属性可以放在花括号包围的块和嵌套标记之前。
[Goals: @select(max:2){fast,good,cheap}.]
{ "Goals: ", @select(max:2){fast,good,cheap}, "." }
请注意,标记内的空白是重要的。注意,下面的示例添加的单个空格如何与先前的示例相比完全改变了其意义。
[Goals: @select(max:2) {fast,good,cheap}.]
{ "Goals: ", @select(max:2), " ", {fast,good,cheap}, "." }
通过调用recon
函数解析RECON编码的字符串。
let event = recon("@event(onClick),@command")!
使用RECON值的方法序列化。
event.recon // returns "{@event(onClick),@command}""
使用值的reconBlock
方法将顶层记录展开(如果有的话)。
event.reconBlock // returns "@event(onClick),@command""
下标通过索引或键获取记录的子项。
let msg = recon("{from: me, to: you}")!
msg[0] // returns Attr("from", "me")
msg["to"] // returns Item("you")
对不存在的键使用下标将返回Value.Absent
。
msg["cc"]
msg[2]
recon("2.0")!["number"]
因为Value.Absent
是一个值,所以下标是“封闭”操作。
recon("{foo: {bar: {baz: win}}}")!["foo"]["bar"]["baz"] // returns Item("win")
Swift字面量到RECON值的隐式转换使得记录构造变得容易。
Value(Attr("img", [Slot("src", "...")]), Slot("width", 10), Slot("height", 10), [Attr("caption", [Slot("lang", "en")]), "English Caption"], [Attr("caption", [Slot("lang", "es")]), "Spanish Caption"])
// returns @img(src:"..."){width:10,height:10,@caption(lang:en)"English Caption",@caption(lang:es)"Spanish Caption"}
Swift库使用以下代数数据类型来表示RECON值:
enum Item {
case Field(Recon.Field)
case Value(Recon.Value)
}
enum Field {
case Attr(String, Value)
case Slot(Value, Value)
}
enum Value {
case Record(Recon.Record)
case Text(String)
case Data(Recon.Data)
case Number(Double)
case Extant
case Absent
}
SP ::= #x20 | #x9
NL ::= #xA | #xD
WS ::= SP | NL
Char ::= [#x1-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
NameStartChar ::=
[A-Z] | "_" | [a-z] |
[#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] |
[#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] |
[#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] |
[#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
NameChar ::= NameStartChar | '-' | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]
MarkupChar ::= Char - ('\\' | '@' | '{' | '}' | '[' | ']')
StringChar ::= Char - ('"' | '\\' | '@' | '{' | '}' | '[' | ']' | '\b' | '\f' | '\n' | '\r' | '\t')
CharEscape ::= '\\' ('"' | '\\' | '/' | '@' | '{' | '}' | '[' | ']' | 'b' | 'f' | 'n' | 'r' | 't')
Base64Char ::= [A-Za-z0-9+/]
Block ::= WS* Slots WS*
Slots ::= Slot SP* ((',' | ';' | NL) WS* Slots)?
Slot ::= BlockValue (SP* ':' SP* BlockValue?)?
Attr ::= '@' Ident ('(' Block ')')?
BlockValue ::=
Attr SP* BlockValue? |
(Record | Markup | Ident | String | Number | Data) SP* (Attr SP* BlockValue?)?
InlineValue ::= Attr (Record | Markup)? | Record | Markup
Record ::= '{' Block '}'
Markup ::= '[' (MarkupChar* | CharEscape | InlineValue)* ']'
Ident ::= NameStartChar NameChar*
String ::= '"' (StringChar* | CharEscape)* '"'
Number ::= '-'? (([1-9] [0-9]*) | [0-9]) ('.' [0-9]+)? (('E' | 'e') ('+' | '-')? [0-9]+)?
Data ::= '%' (Base64Char{4})* (Base64Char Base64Char ((Base64Char '=') | ('=' '=')))?