Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/danielgtaylor/sdt
Structured Data Templates
https://github.com/danielgtaylor/sdt
hacktoberfest json json-schema openapi3 structured-data-templates template-language yaml
Last synced: 2 months ago
JSON representation
Structured Data Templates
- Host: GitHub
- URL: https://github.com/danielgtaylor/sdt
- Owner: danielgtaylor
- License: mit
- Created: 2021-10-17T20:42:58.000Z (about 3 years ago)
- Default Branch: main
- Last Pushed: 2021-12-16T17:31:35.000Z (about 3 years ago)
- Last Synced: 2024-10-05T22:03:23.750Z (3 months ago)
- Topics: hacktoberfest, json, json-schema, openapi3, structured-data-templates, template-language, yaml
- Language: Go
- Homepage:
- Size: 223 KB
- Stars: 4
- Watchers: 3
- Forks: 1
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Structured Data Templates
[![Go Reference](https://pkg.go.dev/badge/github.com/danielgtaylor/sdt.svg)](https://pkg.go.dev/github.com/danielgtaylor/sdt) [![Go Report Card](https://goreportcard.com/badge/github.com/danielgtaylor/sdt)](https://goreportcard.com/report/github.com/danielgtaylor/sdt) ![Build Status](https://github.com/danielgtaylor/sdt/actions/workflows/test.yaml/badge.svg?branch=main) [![codecov](https://codecov.io/gh/danielgtaylor/sdt/branch/main/graph/badge.svg?token=KB0QD0H6HP)](https://codecov.io/gh/danielgtaylor/sdt) [![VS Code Extension](https://img.shields.io/badge/vscode-extension-blue)](https://marketplace.visualstudio.com/items?itemName=danielgtaylor.structured-data-templates)
Structured data templates are a templating engine that takes a simplified set of input parameters and transforms them into a complex structured data output. Both the inputs and outputs can be validated against a schema.
Be sure to check out the [Visual Studio Code extension](https://marketplace.visualstudio.com/items?itemName=danielgtaylor.structured-data-templates) for syntax highlighting and schema validation as you type.
The goals of this project are to:
1. Provide a simple format: it's just JSON/YAML!
2. Give enough tools to be useful:
- Interpolation `${my_value}` & `${num / 2 >= 5}`
- Branching (if/then/else)
- Looping (for/each)
3. Guarantee structural correctness
- The structured data template is valid JSON / YAML
- The input parameters are valid JSON / YAML
- The output of the template is guaranteed to produce valid JSON
4. Provide tools for semantic correctness via schemas
- The input types and values pass the schema
- The template will produce output that should pass the schema
- The output of the template after rendering passes the schema## Structure
A structured data template document is made of two parts: schemas and a template. The schemas define the allowable input/output structure while the template defines the actual rendered output. An example document might look like:
```yaml
schemas:
# Dialect selects the default JSON Schema version
dialect: openapi-3.1
input:
# Input schema goes here
type: object
properties:
name:
type: string
default: world
output:
# Output schema goes here, also supports refs:
$ref: https://api.example.com/openapi.json#components/schemas/Greeting
template:
# Templated output structure goes here
greeting: Hello, ${name}!
```## Installation
You can install via:
```sh
# Get the `sdt` command
$ go get -u github.com/danielgtaylor/sdt/cmd/...# Install as a library
$ go get -u github.com/danielgtaylor/sdt
```## Example
You can run the example like so:
```sh
# Validate the template
$ sdt validate ./samples/greeting.yaml# Generate an example params file
$ sdt example -f yaml ./samples/hello/hello.yaml >params.yaml# Render by passing in a file
$ sdt render ./samples/hello/hello.yaml 50`
- `item.bars.length <= 5 or my_override`
- `"sdt" in name`
- `name startsWith "sdt"`
- `"foo" in my_array`
- `loop.index + 1`See [danielgtaylor/mexpr syntax](https://github.com/danielgtaylor/mexpr#syntax) for details.
### String Interpolation
String interpolation is the act of replacing the contents of `${...}` within strings, where `...` corresponds to an expression that makes use of input parameters. For example:
```yaml
hello: ${name}
```If passed `{"name": "Alice"}` as parameters this would render:
```json
{
"hello": "Alice"
}
```Whenever the string is just one `${...}` statement it will use whatever type it evaluates to in the result, so you are not limited to just strings. If the expression result is `nil`, then the property/item is not included in the rendered output.
It's also possible to add static text or multiple interpolation expressions in a single value:
```yaml
hello: Greetings, ${name}!
```Given the same input that would result in:
```json
{
"hello": "Greetings, Alice!"
}
```#### Tricks
- Force a string output by using more than one expression: `${my_number}${""}`
### Branching
Branching allows one of multiple paths to be followed in the template at rendering time based on the result of an expression. The special properties `$if`, `$then`, and `$else` are used for this. For example:
```yaml
foo:
$if: ${value > 5}
$then: I am big
$else: I am small
```If rendered with `{"value": 1}` the result will be:
```json
{
"foo": "I am small"
}
```Notice that the special properties are completely removed and replaced with the contents of either the `$then` or `$else` clauses. So while in the _template_ `foo` is an object, the end result is that `foo` is a string and would pass the output schema.
If the expression is false and no `$then` is given, then the property is removed from the result.
### Looping
Looping allows an array of inputs to be expanded into the rendered output using a per-item template. The `$for`, `$as`, and `$each` special properties are used for this. For example:
```yaml
squares:
$for: ${numbers}
$each: ${item * item}
```If rendered with `{"numbers": [1, 2, 3]}` the result will be:
```json
{
"squares": [2, 4, 9]
}
```The `$as` property controls the name of the variable holding the current item, which defaults to `item`. A local variable `loop` is also set, which includes an `index`, and whether the item is the `first` or `last` in the array. If using `$as` then the `loop` variable is named `loop_` + the `$as` value. This allows nested loops to access both their own and outer scope's loop variables. For example:
```yaml
things:
$for: ${things}
$as: thing
$each:
id: ${loop_thing.index}-${thing.name}
tags:
$for: ${tags}
$as: tag
$each: ${loop_thing.index}-${loop_tag.index}-${tag}
```Given:
```json
{
"things": [{ "name": "Alice" }, { "name": "Bob" }],
"tags": ["big", "small"]
}
```You would get as output:
```json
{
"things": [
{
"id": "0-Alice",
"tags": ["0-0-big", "0-1-small"]
},
{
"id": "1-Bob",
"tags": ["1-0-big", "1-1-small"]
}
]
}
```### Flatten
The `$flatten` special operator takes an array of arrays and flattens them one level into a single array. This can be useful for a number of scenarios like:
- Adding default items to a `$for` loop output
- Having one item of a `$for` clause generate multiple outputsFor a simple example:
```yaml
my_array:
$flatten:
- [0, 1, 2]
- [3, 4, 5]
- [6, 7, 8]
```This would result in:
```json
{
"my_array": [0, 1, 2, 3, 4, 5, 6, 7, 8]
}
```More complex scenarios are possible when combined with `$for` clauses:
```yaml
# Loop through the items twice, generating one item at a time.
appended_array:
$flatten:
- $for: ${items}
$each: ${item}
- $for: ${items}
$each: ${item * item}
# Loop through the items once, generating a list for each item.
merged_array:
$flatten:
$for: ${items}
$each:
- ${item}
- ${item * item}
```If given:
```json
{
"items": [2, 3, 4]
}
```You would get:
```json
{
"appended_array": [2, 3, 4, 4, 9, 16],
"merged_array": [2, 4, 3, 9, 4, 16]
}
```## Open Questions
1. Should we support macros? Could be done with `$ref` in the template, and we could add a top-level `macros` or `definitions` for document-local refs. They would be drop-in only, no calling with arguments, but would render based on the current params context.
2. Should `nil` results from interpolation be rendered in the final output? Example: `name: ${name}` and what if `name` is `nil`?
3. Support for constants? Values that should always be present in the params that can contain complex and reusable data for the template?
4. Ability to sort `$for` loop output based on some expr?