Swift 中的数据序列化模板语言
使用 DSL 在 .swift
文件中用 Swift 描述数据,然后命令行应用程序将其渲染成任何格式,例如 JSON。
在编写 JSON 或其他类似内容时,我们可能会因重复描述而感到疲惫。
我们经常认为可以像编程语言一样有效地完成这项工作。
jsonnet 是解决该问题的一种预处理器。
Grain 旨在在 Swift 语言中实现这样的功能。
创建一个 swift 文件,然后编写数据,并使用 Grain 工具渲染它。
使用 Swift 的优势在于
序列化 -> cereal -> grain
从 mint 🌱
$ mint install muukii/Grain
从 make
$ make install
未来将添加其他安装方式
$ grain <template file>
$ grain <File 1> <File 2> ...
$ grain <File 1> <File 2> ... --output /path/to/output/
它使用相同的名称将结果写入给定的路径。
Schema.swift
-> Schema.json
创建描述数据的 Data.swift
import GrainDescriptor
serialize {
GrainObject {
GrainMember("value") {
1
}
for i in 0..<10 {
GrainMember("key_\(i)") {
i
}
}
}
}
在终端中将 Data.swift 渲染为 JSON
$ grain Data.swift
{
"key_0" : 0,
"key_1" : 1,
"key_2" : 2,
"key_3" : 3,
"key_4" : 4,
"key_5" : 5,
"key_6" : 6,
"key_7" : 7,
"key_8" : 8,
"key_9" : 9,
"value" : 1
}
serialize
函数声明了什么渲染成数据。
它还具有隐式参数,用于自定义如何编码以及如何保存为文件。
serialize {
// describe what you serialize
}
output 参数接受结果应该如何输出。
serialize(output: .stdout) { ... }
打印到 stdout
serialize(output: .file) { ... }
写入源文件所在目录或指定的输出目录(使用 --output
选项)中的文件
serialize(output: .file(.named("<file_name>")) { ... }
写入文件与上面类似,但使用给定名称的不同名称。
它允许我们多次定义。
这将使用给定名称创建文件。
serialize(output: .file(.named("pattern-1"))) {
}
serialize(output: .file(.named("pattern-2"))) {
}
在 Component.swift 中
import GrainDescriptor
serialize {
GrainObject {
GrainMember("data") {
Results(records: [
.init(name: "A", age: 1),
.init(name: "B", age: 2),
])
}
}
}
// MARK: - Components
struct Record: GrainView {
let name: String
let age: Int
var body: some GrainView {
GrainObject {
GrainMember("name") {
name
}
GrainMember("age") {
age
}
}
}
}
struct Results: GrainView {
let records: [Record]
var body: some GrainView {
GrainObject {
GrainMember("results") {
records
}
}
}
}
$ grain Component.swift
{
"data" : {
"results" : [
{
"age" : 1,
"name" : "A"
},
{
"age" : 2,
"name" : "B"
}
]
}
}
模板文件允许编写 async/await 操作。
GrainDescriptor
内置了 Alamofire
。
例如,我们可以创建使用网络(如获取数据)的序列化数据。
import GrainDescriptor
let response = try await AF.request("https://httpbin.org/get").serializingString().value
serialize {
GrainObject {
GrainMember("result") {
response
}
}
}
import GrainDescriptor
serialize {
Endpoint(methods: [
.init(
method: .get,
summary: "Hello",
description: "Hello Get Method",
operationID: "id",
tags: ["Awesome API"]
)
])
}
// MARK: - Components
public struct Endpoint: GrainView {
public var methods: [Method]
public var body: some GrainView {
GrainObject {
for method in methods {
GrainMember(method.method.rawValue) {
method
}
}
}
}
}
public struct Method: GrainView {
public enum HTTPMethod: String {
case get
case post
case put
case delete
}
public var method: HTTPMethod
public var summary: String
public var description: String
public var operationID: String
public var tags: [String]
public var body: some GrainView {
GrainObject {
GrainMember("operationId") { operationID }
GrainMember("description") { description }
GrainMember("summary") { summary }
GrainMember("tags") { tags }
}
}
}
$ grain endpoints.swift
{
"get" : {
"description" : "Hello Get Method",
"operationId" : "id",
"summary" : "Hello",
"tags" : [
"Awesome API"
]
}
}