dtl-js
v5.4.0
Published
Data Transformation Language - JSON templates and data transformation
Downloads
1,848
Maintainers
Readme
DTL
WebSite | Repository | Wiki | Bug Reports | Live Help (discord) | By Jay Kuri
DTL, short for Data Transformation Language, is a versatile tool ideal for managing and transforming structured data, especially from sources like NoSQL databases and APIs. It seamlessly integrates into your JavaScript projects, whether you're working in a Node.js environment or directly in the browser. Additionally, DTL offers powerful command-line tools that excel in handling various formats, including JSON, YAML, CSV, and plain text. These tools are perfect for processing and exploring existing data sets.
Whether you need something as simple as JSON templating, more complex tasks like format conversion, or intricate data restructuring, DTL stands out as an invaluable resource for developers dealing with any form of structured data manipulation.
If your work involves manipulating structured data, and what development doesn't, DTL offers an efficient and easy to understand toolset to help you.
What DTL looks like
DTL looks like JSON and has a very familiar syntax:
{
"out": {
"name": "(: &( $first_name ' ' $last_name ) :)",
"order_total": "(: $order_subtotal + $tax_amt :)",
"origin": "Import from legacy database",
"address": {
"street": "(: $address1 :)",
"unit": "(: $address2 :)",
"city": "(: $addr_city :)",
"state": "(: $addr_state :)",
"postal": "(: $addr_zip :)"
}
}
}
We'll explore this example in detail a bit later.
Installing DTL
DTL can be installed within your project, or globally for command-line use.
Global Installation
For global access to DTL's command-line tools, install DTL globally using npm or yarn:
Using npm
npm install -g dtl-js
Using yarn
yarn global add dtl-js
Local Installation
To install DTL as part of your project, navigate to your project directory and run:
Using npm
npm install dtl-js
Using yarn
yarn add dtl-js
Verifying the Installation
After installation, you can verify the installation of DTL:
For global installations, check the version to ensure it's installed correctly:
dtl --version
For local installations, you can check your
package.json
to see ifdtl-js
is listed under dependencies.
What DTL is for...
DTL, is built for transforming and manipulating data structures. At its most basic level, DTL can act as a templating tool for JSON or YAML, enabling straightforward data reformatting and restructuring. However, DTL's capabilities extend far beyond simple templating. DTL is ideal for complex tasks such as data extraction, conversion, and preparation for analysis or reporting. Its simple syntax and robust functions facilitate the clear and concise expression of complex data transformations, making DTL an extremely useful tool for a wide range of data processing needs.
Basic Structure
In DTL, transforms are generally defined within a JSON object, which
forms the transform library. Each key in this JSON object is a distinct
transform, functioning like a template or function for how input data should
be outputted. The out
key is special as it's the default transform used by
DTL.apply()
, akin to the main
function in other languages.
In most cases, the value of out
will be an object that looks like your
desired output structure, guiding the transformation of input data to this
format. This library can have multiple transforms, each capable of
referencing and calling one another. When a transform is executed through
DTL.apply()
, the provided data is represented as $.
within the
transform.
Syntax Essentials
1. Happy Tags
Every DTL expression must be enclosed in happy tags: (: :)
. Happy tags
tell DTL to do something. Anything not in Happy tags is passed through
untouched. Happy tags are called that because they resemble happy emoji.
2. Accessing data
Reference data using dot notation, with the root represented as $.
For nested data access, use dot notation, e.g., $user.address.street
Data can be referenced with or without a leading .
, in other words,
$.user
and $user
are equivalent.
And here's a bit of good news: in DTL, trying to access a property of a
non-existent or non-object value won't send you into the dreaded JavaScript
spiral of Cannot read properties of undefined
. In DTL, it just sensibly
returns undefined
. No drama, no crash – just the peace of mind knowing DTL
has got your back in these scenarios.
3. No Commas
In DTL expressions, commas are treated like whitespace and are not necessary.
4. Helper Functions and Transform Calls
Use built-in helper functions for complex operations, e.g.,
math.round($number)
. To call another transform in the library, use
($input_data -> transform_name)
. In this example, $input_data
would be
sent to transform_name
and the result would be the new data.
Operations look like this: $data op $otherdata
, e.g., $number1 + $number2
.
5. Parentheses for Order
Use parentheses to control the order that things should be done, e.g.,
($number1 + $number2) * $number3
. DTL understands mathematical order of
operations, but there's no PEMDAS for data types, so parenthesis will help
make your intentions clearer.
6. Conditional Syntax
For conditionals, use ?(: $condition $truevalue $falsevalue)
. You can use
this to send data to one or another transform depending on some condition:
?(!empty($address) ($. -> process_address) ($. -> no_address))
In this example, ?(!empty($address) ($. -> process_address) ($. -> no_address))
,
DTL checks if $address
is not empty. If it's not, the data
is processed with the process_address
transform; if $address
is empty,
the no_address
transform is used instead. This showcases DTL's ability to
elegantly handle conditional data routing based on the presence or absence
of data.
7. Static and Dynamic Values
A mix of static (literal values) and dynamic (DTL expressions) is common in transforms. Only dynamic expressions use happy tags.
8. Iteration
You can loop or iterate over lists of data. In all helpers that iterate,
like map
or grep
, use $item
, $index
, and $all
for referencing.
$item
is the current item. $index
is the index of the current item and
$all
is the entire list or object.
9. Scope
Transforms only have access to what was given to them (from DTL.apply()
or
another transform in the library) and have no way to access information
other than that. This is a good thing, it means your transform will always
do exactly what you want. Within a transform, $.
refers to the input data
of that transform.
10. DTL Math is Real Math
In DTL, mathematical operations are carried out using Arbitrary Precision Mathematics. This approach is a game-changer, especially when you compare it to JavaScript, where 0.1 + 0.2 equals 0.30000000000000004 (seriously, try it in your web or Node.js console). In DTL, it simply equals 0.3, just as you would logically expect. This means that when you're doing calculations in DTL, you're getting the exactly correct answer every time. DTL's precision makes it ideal for scenarios where mathematical accuracy isn't just a preference, it's a necessity.
11. The concept of empty
In DTL, the concept of empty is key. Empty in DTL means the data has no
meaningful value, e.g. undefined, null, an empty string, array, or object.
The empty()
function returns true if a value is 'empty'
If you've ever wondered why []
evaluates to true or been bitten by
if(formfield)
evaluating to false if someone entered 0, we feel your pain.
In most cases, you really want to know if a variable has a meaningful
value. empty()
is here to help.
Complementing this, the fne()
(First Non-Empty) function scans a list of
values and returns the first one that is not considered empty, streamlining
the selection of valid data from multiple sources. It looks like this:
fne($first_place_to_look $second_place_to_look defaultvalue)
Example in JSON
Here's an example of DTL:
{
"out": {
"name": "(: &( $first_name ' ' $last_name ) :)",
"order_total": "(: $order_subtotal + $tax_amt :)",
"origin": "Import from legacy database",
"address": {
"street": "(: $address1 :)",
"unit": "(: $address2 :)",
"city": "(: $addr_city :)",
"state": "(: $addr_state :)",
"postal": "(: $addr_zip :)"
}
}
}
In this JSON transformation example:
- The
name
field uses the&()
helper to concatenate$first_name
and$last_name
with a space in between. - The
order_total
field does some simple math, adding$order_subtotal
and$tax_amt
together. origin
is a static string, "Import from legacy database".- The
address
field is an object containing multiple expressions. This shows how your transforms mirror your desired output format.
### There is no step 3.
Using DTL is as straightforward as it gets:
1. **Create Your Transform**: Defining how you want your output data to
look. This step involves crafting the template or transformation logic in
a JSON object, specifying how the input data should be processed and
structured.
2. **Call `DTL.apply()`**: Once your transform is ready, the next step is to
apply it to your data. This is done using the `DTL.apply()` method, which
takes your input data and the transform, performs the transformation as defined,
and returns the result.
And that's it — there is no step 3. DTL's design is centered around this
simplicity, making data transformations easy and accessible.
Below is a practical example demonstrating these two steps in a Node.js script:
### Putting It All Together: Example Node.js Script
Now that we've covered the basics of DTL, let's see it in action with a
simple Node.js script. This example demonstrates how to use DTL to
transform some sample data and output the result.
```javascript
// Import the DTL module
const DTL = require('dtl-js');
// Example input data
const inputData = {
first_name: "John",
last_name: "Doe",
order_subtotal: 100,
tax_amt: 20,
address1: "123 Main St",
address2: "Apt 4",
addr_city: "Springfield",
addr_state: "IL",
addr_zip: "62704"
};
// Example DTL transform
const transformLibrary = {
"out": {
"name": "(: &( $first_name ' ' $last_name ) :)",
"order_total": "(: $order_subtotal + $tax_amt :)",
"origin": "Import from legacy database",
"address": "(: $. -> formatAddress :)"
},
"formatAddress": {
"street": "(: $address1 :)",
"unit": "(: $address2 :)",
"city": "(: $addr_city :)",
"state": "(: $addr_state :)",
"postal": "(: $addr_zip :)"
}
};
// Applying the transform to the input data
const transformedData = DTL.apply(inputData, transformLibrary);
console.log("Transformed Data:", JSON.stringify(transformedData, undefined, 4));
This script demonstrates how to import DTL, define a transform library, apply it to some sample data, and output the transformed result. It's a practical example of how DTL can be utilized in a typical Node.js environment to manipulate and transform data structures.
As you can see, integrating DTL into your project is super simple. With its simple syntax and powerful capabilities, DTL is all set to make your job easier. Now, go use it to streamline your code, make your workflows more efficient or just do some awesome stuff!
Full Editor support
Working with DTL is made significantly easier thanks to comprehensive editor support. Full syntax highlighting in the editor bundles not only clarifies DTL syntax but also enhances your coding efficiency. With snippets for all built-in functions, these bundles facilitate a more intuitive and error-free coding experience. We encourage you to download the appropriate bundle for your editor of choice.
Look for DTL in the VSCode extension marketplace, or if you use Vim, Sublime Text or other Textmate compatible editors, you can download the language support files from the Releases page of the DTL Repository Streamline your coding with these powerful features for an improved, error-free DTL coding experience.
Common Problems That DTL Solves
DTL is adept at addressing a wide range of frequently encountered data transformation challenges. Here are some common problems where DTL can be particularly beneficial:
Creating Data Templates: Effortlessly generate structured data from templates. DTL excels in scenarios like auto-generating JSON configurations or preparing data payloads for APIs, making these tasks quick and error-free.
Simple Data Transformations: Whether it's renaming keys in JSON objects, restructuring data (such as flattening nested structures), or performing basic aggregations and calculations, DTL simplifies these common tasks.
Format Conversion: DTL streamlines the conversion between various data formats. Transforming data from JSON to CSV, XML to JSON, or YAML to JSON becomes a hassle-free process.
Data Cleaning and Normalization: Clean or normalize data from diverse sources to fit specific standards. Tasks like removing unwanted fields, standardizing date formats, or converting data types are made straightforward with DTL.
Data Validation: Ensure that incoming data conforms to specified criteria or formats. DTL's data validation capabilities are particularly useful for data from external sources or user inputs.
Extracting Specific Data from Complex Structures: Extracting necessary information from complex or nested data structures, such as certain fields from deeply nested JSON objects, is made efficient and simple.
Automating Repetitive Data Tasks: DTL helps automate repetitive data manipulation tasks, saving time and minimizing the risk of errors in routine operations.
Using DTL, you can address these common data transformation challenges more efficiently, allowing you to focus on the more intricate aspects of your projects.
TL;DR
DTL Repl demo
MOAR Usage
To open the dtlr
data exploration environment:
$ dtlr filename.json
To do bulk data manipulation on the command line:
See available command line options:
$ dtl -h
(Movie examples below use sample movie data from Awesome JSON Datasets )
# Convert movies.json to yaml
$ dtl -o movies.yaml movies.json
# How many movies are listed?
$ dtl -e 'length($.)' movies.json`
# Get all the movies where Bill Murray was in the cast
$ dtl -e 'grep($. "(: member($item.cast `Bill Murray`) :)")' movies.json
# Extract the `name` field in the JSON file in uppercase.
$ dtl -e 'uc($name)' data.json
# Apply the transform expression specified in the `transform.dtl` file to the CSV input file.
$ dtl -f transform.dtl input.csv
# Process `input.json`, output CSV data in `output.csv` including all fields found in input.json.
$ dtl -o output.csv input.json`
# Process `input.json`, output CSV data on stdout using the fields `first_name`, `last_name` and `primary_email`
dtl -O csv -C first_name,last_name,primary_email input.json
Is DTL safe?
Unlike regular code, the output of DTL can only include the information provided to the DTL call, so DTL transforms are much safer to use than the code that would be required to produce the same output. They're also a heck of a lot easier to read... AND since they are self-contained and don't refer to your code, they are safe and easy to share.
DTL can be used within javascript code (node.js and browser, and even inside MongoDB) or it can be used on the command line with the DTL cli tools.
Why should I care?
DTL is interesting for several reasons:
Clarity - DTL is purpose-built for data transformation and only data transformation. It is not intended to be a general-purpose programming language and is therefore simple to learn and free of unnecessary components.
Portable - DTL transforms are self-contained and transferrable between applications. Since they can be stored as JSON, they can even be kept in your database.
Security - DTL transforms only have access to the data that was provided as input. DTL transforms have no other variable or system access, so they are much safer to use than custom code.
Stateless - DTL transforms have no access to previous state, only to the data provided and therefore avoid bugs related to bleed over or inadvertant modification, one of the most common sources of bugs.
Provable - It is trivial to create a DTL transform to verify the output of another. This obviously allows for simple test-creation. What may not be obvious is that these verification transforms can be used to check data at run-time.
Non-linear - DTL transforms define how to arrive at the desired data. They do not define a sequence of steps. This means that each expression is independent and not subject to bugs due to issues that occurred in other expressions.
Stable - DTL has been in use in production since 2013 and has been its own separate project since 2015. It is being used in many production applications, handling many millions of transformations every day.
DTL is a language with an implementation. The DTL npm module is only one implementation of the DTL language. The DTL module contains hundreds of tests that verify the language is behaving properly. This allows DTL to be implemented and verified in any programming language.
Where did DTL come from?
Truth be told, DTL was not originally intended to be it's own thing. DTL began as an expression language inside a meta-programming engine built by Jay Kuri (me) during my work at Ionzero, a company I founded. One of the first applications of this engine was a system built to handle linking other systems together. I created the language out of the need for a way to define how to map data from one system to another without resorting to hard-coded custom code.
I also realized during the course of this work that DTL could be used for far more than I ever had originally envisioned. As a result of this realization, over time, I refined the DTL language and eventually extracted it into a self-contained module that could be used in any system and proceeded to do so.
I decided to release DTL as open source in the hopes that others would find it as useful and as powerful as I have.
The DTL command line tools
As mentioned earlier in this document, if you have installed the DTL
package globally with npm or yarn, you will have two command line tools for
working with DTL. The dtl
cli tool works on bulk data
If you want to just take DTL for a spin without coding you can use the
dtlr
tool. The dtlr
cli tool is an interactive REPL (Read
Execute Print Loop) tool you can use to test out expressions and get
help.
The dtl
cli tool works on bulk data and is designed to process CSV, yaml
and JSON, as well as JSONLines data. It can produce CSV, yaml, JSON and
JSONLines data as well, regardless of whether the input data was the
same type. You can learn more about how to use it by using the
dtl -h
command. Note that by default it sends its output to stdout.
If you'd rather have the output go into a file, use the -o filename.json
option.
Feedback and where to get help
We are always looking for constructive feedback. If you have ideas on how we might improve DTL, please reach out. If you are looking for help on how to use DTL, we also want to hear from you.
For help learning DTL, the dtlr
tool has help built in by
using the .help
command. You can also You can visit the
docs or look at the
DTL Expression Syntax.
You can also view all the helper function docs here.
If you want to see examples of DTL or try it out, head to the DTL website and click the 'Try DTL' button to explore DTL right in your browser.
If you want to see more examples, you can take a look at the Test Suite where you can find an example of just about anything DTL can do.
If you have ChatGPT Plus, you can talk to the DTL Helper to get help with DTL.
If you want something a bit more real-time and real-person, you can talk with us on the DTL discord.
And, if you encounter a bug, please don't hesitate to file an Issue.