When you’re working with TOML (Tom’s Obvious, Minimal Language), one of the key concepts that might seem a bit tricky at first is how dotted keys are used. These keys don’t just act as regular strings—they actually serve a bigger purpose, helping you define tables automatically without needing extra table headers.
If you’ve ever worked with hierarchical data structures like JSON or YAML, you’ll be familiar with the concept of nesting objects. TOML’s dotted keys bring a similar feel to the table, but with a few unique mechanics that make things both simpler and more flexible. Let’s understand dotted keys in TOML tables, how they work, when to use them, and how they help structure nested data in TOML configuration files.
What Are TOML Tables?
At the core of TOML are tables, which store data in key-value pairs. A table in TOML is defined by a header enclosed in square brackets ([]
). The header marks the start of a new table, and the lines following it contain key-value pairs associated with that table. Tables can also be nested within one another, creating a hierarchical structure.
Basic Table Example
Let’s start with a simple example of a TOML file:
[fruit]
apple = "red"
orange = "orange"
Here, we have a table called [fruit]
, which contains two key-value pairs:
apple = "red"
orange = "orange"
TOML tables are unordered, meaning the order of key-value pairs within a table doesn’t matter. However, it’s still best practice to organize them in a logical, consistent way to improve readability.
Keys and Values in TOML
In TOML, keys are used to identify values, which are assigned using the =
symbol. Keys can be strings, integers, floats, dates, or other valid data types.
Key Example
name = "Lalya"
age = 3
Here,
- The key
name
has the value"Lalya"
, a string. - The key
age
has the value3
, an integer.
Keys can also contain spaces, but it’s best practice to avoid them. If spaces are necessary, they can be included inside quotes (e.g., "first name"
).
Dotted Keys in TOML Tables : Creating Nested Structures
One of the powerful features of TOML is its ability to define nested tables using dotted keys. This allows you to create hierarchical structures, making your configuration more organized and readable.
When you use a dotted key, TOML automatically creates tables for each part of the key before the last one, as long as those tables haven’t already been defined. This is useful when you want to group related settings under a single category, but you don’t want to manually define each table.
Dotted Key Example
fruit.apple.color = "red"
fruit.apple.size = "medium"
fruit.orange.color = "orange"
This will automatically create the following structure:
A table called fruit
A sub-table called fruit.apple
- Keys like
color
andsize
A sub-table called fruit.orange
- A key for
color
It’s important to note that dotted keys don’t require separate [table “” not found /]
headers, but they create a nested structure for you. This can simplify configuration files and reduce the need for manual table definitions.
How Do Dotted Keys Work in TOML Table?
Dotted keys allow you to define hierarchical structures within your tables without the need to explicitly define multiple tables at each level. A dotted key is a key that contains multiple parts separated by periods (.
). Each segment of the dotted key represents a level in the hierarchy, and TOML automatically creates tables for these levels when you use dotted keys.
Let’s see a simple example.
fruit.apple.color = "red"
fruit.apple.taste.sweet = true
Here,
fruit
is a table.fruit.apple
is a sub-table inside thefruit
table.fruit.apple.color
creates a keycolor
inside thefruit.apple
table, and it’s assigned the value"red"
.fruit.apple.taste.sweet
creates another keysweet
inside thefruit.apple.taste
table, which gets the valuetrue
.
In this case, TOML handles the creation of the tables for you:
- It creates the
fruit
table. - Then, it creates a sub-table
apple
inside thefruit
table. - It further creates the sub-table
taste
insidefruit.apple
.
This hierarchical structure helps organize data cleanly, and you don’t need to manually define each table as you would in traditional formats like JSON or YAML.
Defining Tables and Sub-Tables: Restrictions
You cannot redefine a table once it has been created. If a table is already defined, you cannot redefine it using a [table “” not found /]
header or dotted keys.
Example (Incorrect/Invalid):
# Note – Considering the above examples, the table is already defined, so the following examples are invalid.
[fruit.apple] # Invalid, because fruit.apple has already been defined
color = "green"
[fruit.apple.taste] # Invalid, fruit.apple.taste already defined
sweetness = "high"
However, sub-tables can be added under an existing table, even if it was defined using dotted keys.
Defining Sub-Tables
If you’ve already used dotted keys to create tables, you can still use the [table “” not found /]
header to add sub-tables inside the already created tables.
[fruit] # This is the main table.
apple.color = "red"
apple.taste.sweet = true
[fruit.apple.texture] # Defines a sub-table for texture under apple.
smooth = true
Here,
- The
fruit
table is defined withapple.color
andapple.taste.sweet
. [fruit.apple.texture]
defines a sub-table withinfruit.apple
, and the keysmooth
is set totrue
.
Sub-Tables and Nested Headers
If you want to define sub-tables manually, you can use the [table “” not found /]
header. A sub-table can be defined by including a table header within an existing table.
[fruit]
apple = "red"
orange = "orange"
[fruit.apple.texture]
smooth = true
rough = false
Here,
- The main
fruit
table containsapple
andorange
entries. - The
[fruit.apple.texture]
sub-table defines properties related to the texture of apples (smooth
andrough
).
This feature helps to create a clear hierarchy in our configuration file, especially for more complex configurations.
Real-World Example: Fruit Configuration
In this example, we organize information about fruit using both dotted keys and [table “” not found /]
headers.
[fruit] # The main fruit table
apple.color = "red"
apple.taste.sweet = true
apple.size = "medium"
[fruit.apple.texture] # A sub-table for texture within apple
smooth = true
rough = false
[fruit.orange] # Another fruit: orange
color = "orange"
taste.sour = true
Equivalent JSON:
{
"fruit": {
"apple": {
"color": "red",
"taste": {
"sweet": true
},
"size": "medium",
"texture": {
"smooth": true,
"rough": false
}
},
"orange": {
"color": "orange",
"taste": {
"sour": true
}
}
}
}
So, in general, here are the key points to remember:
- Dotted keys automatically create tables for each key part (unless they’re already defined).
- Tables cannot be redefined once they exist.
- Sub-tables can be defined using
[table “” not found /]
headers within already created tables.
- Best practice: Use dotted keys for simple cases, but use the
[table “” not found /]
header when defining sub-tables for better clarity and organization.
Best Practices
- Use Dotted Keys Sparingly: Dotted keys are great for defining nested structures, but avoid going overboard. If the hierarchy becomes too deep, it may be better to use
[table “” not found /]
headers for clarity.
- Avoid Redundant Tables: Refrain from defining the same table multiple times, whether using the
[table “” not found /]
header or dotted keys. Once defined, it should remain unique.
- Stick to Consistent Naming: TOML allows flexibility in naming tables and keys, but consistency is key. Follow conventions such as lowercase letters and underscores for readability and ease of maintenance.
Conclusion
Dotted keys in TOML provide an elegant and efficient way to structure hierarchical data without the need to manually define each level. By understanding the rules and limitations of dotted keys, you can simplify your configuration files while maintaining a clean, logical structure.
Remember that while TOML allows flexibility, following best practices ensures that your configuration files remain readable and maintainable. Avoid redefining tables, ensure your hierarchy is logical, and use sub-tables to add depth without cluttering your structure.