Skip to content

Instantly share code, notes, and snippets.

@vzsg
Last active November 25, 2020 13:06
Show Gist options
  • Save vzsg/8a2ab5089e522787ec87c5e2ceb003e0 to your computer and use it in GitHub Desktop.
Save vzsg/8a2ab5089e522787ec87c5e2ceb003e0 to your computer and use it in GitHub Desktop.
Automatically encoding data to JSON with Leaf (for embedding into scripts)
import Leaf
import Foundation
final class JSONTag: TagRenderer {
private let encoder = JSONEncoder()
func render(tag: TagContext) throws -> EventLoopFuture<TemplateData> {
try tag.requireNoBody()
try tag.requireParameterCount(1)
let json = try encoder.encode(tag.parameters[0])
return tag.future(TemplateData.string(String(data: json, encoding: .utf8) ?? ""))
}
}
extension TemplateData: Encodable {
public func encode(to encoder: Encoder) throws {
if isNull {
var svc = encoder.singleValueContainer()
try svc.encodeNil()
return
}
if let array = self.array {
var ukc = encoder.unkeyedContainer()
try ukc.encode(contentsOf: array)
return
}
if let dict = self.dictionary {
var kvc = encoder.container(keyedBy: String.self)
try dict.forEach {
try kvc.encode($1, forKey: $0)
}
}
// FIXME: this will encode the String "5" as the number 5. This seems
// to be less harmful than encoding everything as Strings...
// There seems to be no way around the fuzziness of TemplateData.
if let int = self.int {
var svc = encoder.singleValueContainer()
try svc.encode(int)
return
}
if let double = self.double {
var svc = encoder.singleValueContainer()
try svc.encode(double)
return
}
if let string = self.string {
var svc = encoder.singleValueContainer()
try svc.encode(string)
return
}
if let data = self.data, let string = String(data: data, encoding: .utf8) {
var svc = encoder.singleValueContainer()
try svc.encode(string)
return
}
}
}
// ...
var leafTag = LeafTagConfig.default()
leafTag.use(JSONTag(), as: "json")
services.register(leafTag)
// ...
import Foundation
extension String: CodingKey {
public init?(stringValue: String) {
self.init(stringLiteral: stringValue)
}
public init?(intValue: Int) {
self.init(intValue)
}
public var stringValue: String {
return self
}
public var intValue: Int? {
return Int(self)
}
}
// ...
router.get("jsonleaf") { req -> Future<View> in
struct Context: Encodable {
let params: ChartParams
}
struct ChartParams: Encodable {
let labels: [String]
let values: [[Int]]
}
let context = Context(params: ChartParams(labels: ["Jan", "Feb", "Mar"], values: [[1,3,4], [2,3,4], [5,4,3]]))
return try req.view()
.render("test", context)
}
// ...
<!DOCTYPE html>
<html>
<head>
<title>JSON tag example</title>
</head>
<body>
<h1>Here's some JSON data</h1>
<pre>#json(params)</pre>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment