Master in TOML Tables: A Complete Guide to Their Types and Usage

Table of Contents

TOML (Tom’s Obvious, Minimal Language) is a popular configuration file format known for its simplicity and readability. One of TOML’s core features is its tables, which help structure data efficiently. This guide will walk you through TOML tables, their types, and how to use them with detailed examples.

TOML Tables

TOML Tables (also called dictionaries or hash tables) store data in key/value pairs, where a ‘key’ acts as a label for a specific ‘value.’ Tables are defined by headers, written in square brackets [ ] on their own line, which typically appear at the beginning of a table. This helps distinguish tables from arrays, which hold a list of values without labels or keys.

Basic Syntax of a TOML Table

TOML
[Table name]
key1 = value1
key2 = value2
key3 = value3

Note: The key difference between a normal list of key-value pairs and a table is that, in a table, the key-value pairs are related to each other and grouped together under a common section. This is why they are denoted with a table header.

TOML
[table-1]  # This is a table (a section)
key1 = "some string"  # key1 stores a text value
key2 = 123  # key2 stores a number

[table-2]  # Another table (a separate section)
key1 = "another string"  # key1 has a different value here
key2 = 456  # key2 has a different number

TOML tables are like containers that hold key-value pairs. You can think of them as sections in a document, similar to headings in a text file.

Tables are created using square brackets ([ ]):

  • Example: [table-1] creates a table named table-1.

Key-value pairs inside a table define data:

  • key1 = "some string" → This means key1 has the text “some string”.
  • key2 = 123 → This means key2 has the number 123.

Different tables group different sets of key-value pairs:

table-1 has it’s own two key-value pairs:

  • key1"some string"
  • key2123

table-2 also has it’s own two key-value pairs:

  • key1"another string"
  • key2456

Note: Each table acts as a separate group of related information. Everything below a header belongs to that table until another header appears or the file ends. The keys inside one table do not affect another, and the key/value pairs within a table can be in any order and may not remain as originally written (meaning you can change their order as needed).

Simple Example: Storing User Information in a TOML Table

TOML
[User]
name = "amol"
age = 30
email = "[email protected]"

Here,

Table Header ([User]):

  • This defines a section called "User", grouping related key-value pairs together.
  • Everything under this header belongs to this table.

Key-Value Pairs (name, age, email):

  • Each key represents a specific piece of information.
  • Each value holds the actual data.
  • "amol" is a string, 30 is a number, and "[email protected]" is another string.

Why Use a Table?

  • Without a table, these key-value pairs would be unorganized and unrelated.
  • The [User] header makes it clear that name, age, and email all belong to one entity (User).

Types of Tables in TOML

TOML supports multiple types of tables:

  1. Standard Tables
  2. Nested Tables
  3. Inline Tables
  4. Array of Tables
  5. Empty Tables
  6. Top-Level Tables or Root Tables
  7. Tables with Dotted Keys

Let’s go through each type in detail (We will explore Inline Tables and Arrays of Tables in more detail in later sections).

Standard Tables

These are the most common tables in TOML. They are defined using square brackets ([]) and help in grouping related configurations.

TOML
#This defines a server table with two key-value pairs.
[server]
ip = "192.168.1.1"
port = 8080

Here, server is a table containing ip and port as key-value pairs.

Nested Tables

TOML allows you to nest tables inside other tables to create hierarchical data structures.

TOML
[database]
name = "myapp"

[database.connection]
host = "127.0.0.1"
port = 3306

Here, database.connection is a nested table under database. This defines a database table containing a nested connection table. This structure makes it easy to manage related settings. The equivalent hierarchical structure would look like:

TOML
database
  ├── connection
       ├── host = "127.0.0.1"
       ├── port = 3306

JSON representation:

JSON
{
  "database": {
    "name": "myapp",
    "connection": {
      "host": "127.0.0.1",
      "port": 3306
    }
  }
}

Inline Tables

An inline table is a compact way to define key-value pairs in a single line. These are enclosed within curly brackets {} and should be used for small datasets.

TOML
#Inline Table
info = { name = "amol", age = 30, email = "[email protected]" }

Here, info is an inline table containing three key-value pairs.

Equivalent standard TOML Table Representation:

TOML
# This is equivalent to standard TOML Table (It's just for the sake of understanding)
[info]
name = "amol"
age = 30
email = "[email protected]"

Use Cases:

  • Inline tables are best for defining small, one-liner configurations.
  • They improve readability when used sparingly.

Note: Inline tables improve readability for small tables but should be avoided for large datasets.

Array of Tables

TOML also supports arrays of tables, which allow you to define multiple instances of a structured table. This is useful when you need a list of similar objects.

TOML
[[employees]]
name = "Amol"
position = "Engineer"

[[employees]]
name = "Niyati"
position = "Designer"

Each [[employees]] block represents a separate table within the array.

Equivalent JSON Representation:

JSON
{
  "employees": [
    {"name": "Amol", "position": "Engineer"},
    {"name": "Niyati", "position": "Designer"}
  ]
}

Here, we define multiple employees, each as an entry in an array.

Empty Tables

Empty tables are allowed in TOML, meaning they can exist without any key/value pairs

TOML
[empty_table]  # This table has no keys or values, and that's fine

This creates an empty table called empty_table.

Top-Level Tables or Root Tables

The top-level table (or root table) is the first section of the document. It’s unique because it doesn’t have a header (no square brackets) and defines key-value pairs directly. It starts at the very beginning of the document and ends before the first named table (or the end of the file). Unlike other tables, it is nameless and cannot be relocated.

TOML
# Top-level table starts here
name = "Lalya"
breed = "Desi Indian Dog"

# Top-level table ends here 
[owner]         # This is a separate table after the root table.
name = "Amol Pawar"
member_since = 2024-08-04

Here,

  • The root table holds the basic details, like name and breed.
  • The [owner] table follows and defines details about the dog’s owner.

Top-level tables define global information and appear before the first named table. They cannot be relocated. A top-level table is unnamed and should always be at the start of the file, before any named tables.

Tables with Dotted Keys

In TOML, dotted keys allow you to define tables at various levels within a hierarchy by using dot notation. TOML will create tables for each key part before the last one, as long as those tables haven’t already been defined.

Creating Tables with Dotted Keys

When you use dotted keys, they automatically create and define tables for each part of the key, up to the second-last part. This happens only if the tables were not previously created.

TOML
fruit.apple.color = "red"    # Creates the "fruit" table and the "fruit.apple" table

In this case, TOML interprets this as:

  • A fruit table (if it doesn’t exist).
  • A nested fruit.apple table (if it doesn’t exist).
  • A key-value pair for color inside fruit.apple.

Defining Nested Tables with Dotted Keys

When you extend the dotted keys, TOML continues creating sub-tables as needed.

TOML
fruit.apple.taste.sweet = true   # Creates fruit.apple.taste table

This results in:

  • The fruit table (if not already defined).
  • The fruit.apple table (if not already defined).
  • The fruit.apple.taste table (if not already defined).
  • The key sweet inside fruit.apple.taste with the value true.

Defining Tables and Sub-Tables: Restrictions

You cannot redefine a table that has already been created, either through a dotted key or using the [table “” not found /]

header.

Example (Incorrect/Invalid):

TOML
# 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.

TOML
[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 with apple.color and apple.taste.sweet.
  • [fruit.apple.texture] defines a sub-table within fruit.apple, and the key smooth is set to true.

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.

TOML
[fruit]
apple = "red"
orange = "orange"

[fruit.apple.texture]
smooth = true
rough = false

Here,

  • The main fruit table contains apple and orange entries.
  • The [fruit.apple.texture] sub-table defines properties related to the texture of apples (smooth and rough).

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.

TOML
[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:

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.

Understanding Table Naming Rules in TOML

TOML table names follow the same rules as TOML keys. This means:

  • They must be unique within the document.
  • They can be quoted if they contain special characters (e.g., spaces or dots).

Example: Nested Tables with a Special Character in the Name

TOML
[dog."tater.man"]
type.name = "pug"

JSON representation:

JSON
{
  "dog": {
    "tater.man": {
      "type": {
        "name": "pug"
      }
    }
  }
}

Here,

[dog."tater.man"] (Nested Table with Quoted Key)

  • dog is the top-level table.
  • "tater.man" is a subtable inside dog.
  • The quotes around "tater.man" are necessary because it contains a dot (.), which TOML normally treats as a separator for nested tables.

type.name = "pug" (Nested Key-Value Pair)

  • Inside the "tater.man" subtable, we create another subtable type.
  • Inside type, we define name = "pug".

Note: 

  • Quoted table names allow you to use dots (.) as part of the name, instead of indicating nested tables.
  • Without quotes, tater.man would be interpreted as two nested tables (dog → tater → man). However, by using quotes, "tater.man" is treated as a single key under dog, making it work similarly to JSON.

Whitespace and Formatting Rules

Whitespace around the key is ignored. Similarly, indentation is treated as whitespace and ignored. However, it is best practice to avoid extraneous whitespace.

TOML
[a.b.c]       # Best practice: simple and clear
[ d.e.f ]     # Same as [d.e.f] (extra spaces ignored)
[ g .  h  . i ] # Same as [g.h.i] (extra spaces ignored)
[ j . "ʞ" . 'l' ] # Same as [j."ʞ".'l'] (mixed quotes are allowed)

Here,

  • Spaces don’t matter, but avoid unnecessary spaces to keep things readable.
  • Quotes (" ", ' ') are allowed in table names, useful when special characters are needed.

So,

  • Use dot notation (.) to create nested tables.
  • Whitespace around table names is ignored, but best practice is to keep it clean.
  • Quotes can be used in table names if special characters are needed.

Avoid Defining a Table More Than Once

Just like keys, tables cannot be defined more than once. Doing so is invalid and will result in an error in TOML.

Incorrect Example 1:

You can’t redefine the [fruit] table like this:

TOML
[fruit]  
apple = "red"  

[fruit]  # This is invalid!
orange = "orange"

Incorrect Example 2:

This would also be invalid if you define a table and try to redefine part of it:

TOML
[fruit]  
apple = "red"  

[fruit.apple]  # This is also invalid!
texture = "smooth"

So, how do we correct this? As we now know, if a table already exists, trying to define it again results in an error. To avoid this, we can use a different table name, like ‘fruits‘ instead of ‘fruit.’

TOML
[fruit]      # Define fruit table
apple = "red"

[fruits.apple]  # Nested table inside "fruits" for apple-specific info
texture = "smooth"

This is just an example for understanding purposes, but the key takeaway is to avoid re-defining the same table name more than once.

Defining Tables Out-of-Order (Valid but Discouraged)

Although TOML allows you to define tables in any order, it’s discouraged to do so. Organizing your tables properly makes your code more readable and maintainable.

Example (Valid but Discouraged):

You can technically define tables out of order, but it’s not considered best practice.

TOML
[fruit.apple]   # Defining fruit.apple first
color = "green"

[animal]        # Then defining animal
species = "dog"

[fruit.orange]  # Finally defining fruit.orange
color = "orange"

While this is valid TOML syntax, the recommended approach is to group related tables together in order.

Example (Recommended):

It’s better to define related tables consecutively for clarity.

TOML
[fruit.apple]   # First define fruit.apple
color = "green"

[fruit.orange]  # Then define fruit.orange
color = "orange"

[animal]        # Then define animal
species = "dog"

This way, it is easier to follow, especially for people who are reading or editing the file.

Super-Tables Not Required

In TOML, you don’t always have to specify every level of hierarchy for your tables. TOML can infer the parent tables (super tables) automatically, making things simpler for you.

Basically, TOML knows how to handle nested tables without you defining all of them:

If you specify [x.y.z.w], TOML automatically understands the hierarchy:

  • xx.yx.y.zx.y.z.w.
  • You don’t need to declare [x], [x.y], and [x.y.z] for this to work. Just go straight to the deepest level you need.
TOML
[x.y.z.w]  # This works without needing to define [x], [x.y], or [x.y.z]

You don’t need to define [x], [x.y], or [x.y.z] in advance; TOML will infer them.

Defining a super-table afterward is fine:

  • You can define a higher-level table after nested ones, and it will still work. TOML will connect them properly.
TOML
[x.y.z.w]  # Defines the deepest nested table


[x]   # This works even after defining [x.y.z.w]

Inline Tables

Here, we will look at how inline tables work and when you should use them. Let’s dive deep into the concept.

What Are Inline Tables?

Inline tables are essentially a shorthand way of defining tables in TOML. Normally, tables in TOML are defined with square brackets and the data is organized over multiple lines. However, inline tables let you define key-value pairs within a single line, wrapped in curly braces {}. This format is particularly useful when you need to represent a small set of related data without the verbosity of a full table.

For instance, let’s consider a simple example of defining a person’s name using an inline table:

TOML
name = { first = "Amol", last = "Pawar" }

This is equivalent to the more traditional multi-line table definition:

TOML
[name]
first = "Amol"
last = "Pawar"

As we can see, inline tables provide a more compact syntax, making the configuration file shorter and cleaner when dealing with small, simple data structures.

Key Characteristics of Inline Tables

Here are some important points to remember about inline tables:

Single-Line Definition

An inline table must be defined on a single line. This is a key feature that distinguishes it from standard tables, which often span multiple lines.

TOML
point = { x = 1, y = 2 }

It’s important to note that inline tables are not meant to be split across multiple lines. If you find yourself wanting to break up the content, that’s a good indication that you should use a standard table instead.

No Trailing Commas

Inline tables do not allow a trailing comma after the last key-value pair. This means that the last entry should not be followed by a comma, unlike in many programming languages where trailing commas are allowed to make the syntax more flexible.

TOML
point = { x = 1, y = 2 }  # Correct

# point = { x = 1, y = 2, }  # Incorrect - trailing comma is not allowed

No Newlines Within the Table

Inline tables should not have any newlines unless they are inside the value itself. For example, if the value of a key is a string that includes line breaks, that’s perfectly fine. But you cannot have multiple key-value pairs or nested tables on separate lines within an inline table.

TOML
animal = { type.name = "dog" }

# Equivalent standard table

[animal]
type.name = "dog"

This is valid because it’s all contained in one line. However, splitting this into multiple lines would violate the format and is not allowed:

TOML
# This is incorrect:
# animal = { 
#   type.name = "dog" 
# }

Limitations with Sub-Tables

Inline tables are fully self-contained. This means that all keys and their values (including nested tables) must be included within the inline table itself. You cannot define keys or sub-tables outside the curly braces. If you try to add new sub-tables or new keys outside of an inline table, it will result in an error.

For example:

TOML
[product]
type = { name = "Nail" }

# type.edible = false  # INVALID

Here, trying to add type.edible outside of the inline table definition is not allowed. If you need to define multiple levels of keys, you’ll have to resort to standard table syntax.

Also, inline tables cannot be used to add keys or sub-tables to an already-defined table. Means, inline tables must be fully defined when they are created. Once you define an inline table, you cannot later add new keys or sub-tables to it.

TOML

So, once you create an inline table, you CAN NOT add new keys or sub-tables to it later.

When to Use Inline Tables

Inline tables are best used for small, simple groupings of data that don’t require the complexity of a full table definition. They’re ideal for cases where:

  • You want to keep the configuration compact and concise.
  • The data structure is small and won’t benefit from being split into multiple lines or tables.
  • The data is logically grouped together but doesn’t need to be nested or expanded.

For example, if you’re defining a point with x and y coordinates, an inline table works perfectly:

TOML
point = { x = 1, y = 2 }

However, if you find yourself needing more complex nested data or want to include many more key-value pairs, it’s better to use the standard multi-line table format:

TOML
[point]
x = 1
y = 2

Best Practices for Inline Tables

While inline tables are a powerful feature in TOML, they come with certain best practices to ensure your configuration files remain readable and maintainable:

  1. Use Inline Tables for Simple, Grouped Data
    They should be reserved for cases where the data structure is simple and doesn’t require nesting. Avoid using them for complex, multi-level data structures.
  2. Don’t Overuse Inline Tables
    While they’re compact, excessive use of inline tables can make your TOML file harder to read and maintain, especially if it results in very long single lines. When in doubt, opt for standard tables.
  3. Maintain Consistency
    If you choose to use inline tables in your configuration, try to maintain consistency throughout the file. Mixing inline and standard tables in the same configuration might confuse the reader, especially if it’s not clear why one approach was chosen over the other.

Arrays of Tables in TOML

Here, we’ll break down how to use arrays of tables, subtables, and inline tables in TOML. We’ll explore some common mistakes, how TOML ensures data consistency, and how you can use these features effectively.

What are Arrays of Tables in TOML?

In TOML, a table is a collection of key-value pairs, similar to a dictionary or JSON object. An array of tables is a list of such tables, each having the same structure but different values.

This is useful when defining multiple items of the same type, such as a list of users, servers, configurations, or even a collection of products, fruits, or other items.

Syntax

Arrays of tables are defined using double square brackets [[ ... ]]. Each set of double square brackets creates a new table in the array.

TOML
[[servers]]
host = "192.168.1.1"
port = 8080

[[servers]]
host = "192.168.1.2"
port = 9090

Here,

  • We define an array of tables named servers.
  • Each [[servers]] block represents an entry in the array.
  • Each server has a host and port.

The resulting structure in JSON would look like this:

JSON
{
  "servers": [
    { "host": "192.168.1.1", "port": 8080 },
    { "host": "192.168.1.2", "port": 9090 }
  ]
}

One more simple example for clarity.

TOML
[[fruit]]
name = "Apple"
color = "Red"

[[fruit]]
name = "Banana"
color = "Yellow"

[[fruit]]
name = "Grapes"
color = "Green"

In JSON, this would look like:

JSON
{
  "fruit": [
    { "name": "Apple", "color": "Red" },
    { "name": "Banana", "color": "Yellow" },
    { "name": "Grapes", "color": "Green" }
  ]
}

How It Works

Arrays of tables begin with the first occurrence of that header, which initializes the array and adds its first table element. Each subsequent occurrence creates and adds a new table element to the array, with tables inserted in the order they appear.

Means,

  • First instance of [[table_name]] → Defines the array and its first table element.
  • Subsequent instances of [[table_name]] → Add more tables to the array.
  • Insertion order matters → The tables appear in the order they are written.
TOML
[[products]]
name = "Apple MacBook Air (M2)"
sku = 7393945837

[[products]]  # empty table within the array

[[products]]
name = "Asus Zenbook 14X OLED"
sku = 393857482

color = "space gray"    # For readability and clarity, a space was added above, but it is part of the same products table (which contains 'name' as 'Asus Zenbook 14X OLED')

Here,

  • [[products]]: This creates the first table inside an array called products. The table has two keys: name and sku.
  • [[products]] (Empty Table): This is an empty table inside the same products array. No key-value pairs are defined here.
  • [[products]] (Another Table): This creates another table inside the same array. It has name, sku, and color as keys.

If you convert this TOML structure to JSON, it would look like this:

JSON
{
  "products": [
    { "name": "Apple MacBook Air (M2)", "sku": 7393945837 },
    { },
    { "name": "Asus Zenbook 14X OLED", "sku": 393857482, "color": "gray" }
  ]
}

Notice how JSON represents the array of tables. Each object in the array corresponds to a table in TOML, and the empty table is represented as an empty object {} in JSON.

Note :

  • The [[products]] syntax is used to define an array of tables, with each table being a separate entry in the array.
  • The tables are ordered by their appearance, and you can have empty tables within the array as well.

Nested Arrays of Tables

First, let’s do a quick recap. As we know, we can define subtables within a table, allowing us to group related data under a single parent table. A subtable is essentially a table within a table and is indicated using dot notation ([parent.subtable]).

Now, when we reference an array of tables, it always refers to the most recently defined table element in that array. This behavior allows us to define subtables (nested tables) and even sub-arrays of tables (nested arrays of tables) within the last added table.

Let’s understand this:

  • Each [[array_name]] creates a new table inside an array.
  • Any sub-table ([array_name.subtable]) or sub-array ([[array_name.subtable]]) added immediately after a [[array_name]] entry applies to the most recently created table.

Example 1: Defining Sub-Tables

TOML
[[company]]
name = "TechCorp"

[company.address]
city = "New York"
zip = "10001"

[[company]]
name = "softAai"

[company.address]
city = "Pune"
zip = "411001"

Equivalent JSON Representation:

JSON
{
  "company": [
    {
      "name": "TechCorp",
      "address": {
        "city": "New York",
        "zip": "10001"
      }
    },
    {
      "name": "softAai",
      "address": {
        "city": "Pune",
        "zip": "411001"
      }
    }
  ]
}

Here, in the above TOML file,

  • [[company]] defines an array of tables named company.
  • The first instance (TechCorp) gets a sub-table company.address with "New York".
  • The second instance (softAai) gets a different company.address with "Pune".
  • The company.address sub-table always applies to the last [[company]] entry. As you can see, it belongs to the most recently added or created table instance, which is why it is part of the second element (softAai) in the array of tables.

Example 2: Defining a Sub-Array of Tables

TOML
[[library]]
name = "Pune University Library"

[[library.books]]
title = "The Guide"
author = "R.K. Narayan"

[[library.books]]
title = "Malgudi Days"
author = "R.K. Narayan"

[[library]]
name = "Pune City Library"

[[library.books]]
title = "Godan"
author = "Munshi Premchand"

Equivalent JSON Representation:

JSON
{
  "library": [
    {
      "name": "Pune University Library",
      "books": [
        {
          "author": "R.K. Narayan",
          "title": "The Guide"
        },
        {
          "author": "R.K. Narayan",
          "title": "Malgudi Days"
        }
      ]
    },
    {
      "name": "Pune City Library",
      "books": [
        {
          "author": "Munshi Premchand",
          "title": "Godan"
        }
      ]
    }
  ]
}

Here, in the above TOML file,

  • [[library]] defines an array of libraries.
  • Each [[library.books]] entry belongs to the most recent [[library]].
  • "Pune University Library" has two books, while "Pune City Library" has one book.
  • This results in a nested array of tables, which means you can have tables inside an array that itself contains another table.

Let’s look at a slightly more complex example, where we combine sub-tables and nested arrays of tables.

TOML
[[fruits]]
name = "mango"

[fruits.physical]  # Subtable for the physical attributes of the fruit
color = "yellow"
shape = "oval"

[[fruits.varieties]]  # Nested array of tables for the varieties of the fruit
name = "alphonso"

[[fruits.varieties]]
name = "dasheri"

[[fruits]]
name = "banana"

[[fruits.varieties]]
name = "robusta"

Here,

  • The first [[fruits]] creates a table for a mango, with a name field.
  • The [fruits.physical] subtable defines physical properties of the mango, like its color and shape. It’s nested within the fruits table.
  • The [[fruits.varieties]] creates an array of tables to list different varieties of the mango, such as “alphonso” and “dasheri”.
  • Another [[fruits]] is added for a banana, which also has varieties defined.

Equivalent JSON Representation:

JSON
{
  "fruits": [
    {
      "name": "mango",
      "physical": {
        "color": "yellow",
        "shape": "oval"
      },
      "varieties": [
        { "name": "alphonso" },
        { "name": "dasheri" }
      ]
    },
    {
      "name": "banana",
      "varieties": [
        { "name": "robusta" }
      ]
    }
  ]
}

In JSON, you can see that the fruits array now contains objects, each of which can have other nested objects (like physical) and arrays (like varieties).

Common Mistakes and How to Avoid Them

While TOML is simple, there are some strict rules about how arrays and tables are defined. Here are a few common mistakes that TOML will throw errors for:

Parent Missing or Misplaced Order

TOML
# INVALID TOML DOC

[fruit.physical]  # subtable, but to which parent element should it belong?
color = "red"
shape = "round"

[[fruit]]  # parser must throw an error upon discovering that "fruit" is
           # an array rather than a table
name = "apple"

Now, you may be wondering: Why is this invalid?”

  • The subtable [fruit.physical] doesn’t know where it belongs because the parent fruit table hasn’t been defined yet.
  • The [[fruit]] entry is incorrect because it tries to define a table inside an array, but the fruit array was not set up beforehand.

In short, in TOML, a table (like fruit) must be defined before any subtables or arrays of tables inside it. The parser ensures that all parent elements exist before any child elements are defined.

Appending to an Already Defined Array

Basically, attempting to append to a statically defined array, even if the array is empty, should result in a parse-time error.

TOML
# INVALID TOML DOC
fruits = []

[[fruits]] # Not allowed

Here, you’re trying to append a new table [[fruits]] to an already defined empty array (fruits = []). Since fruits is statically defined as an array, trying to add a table to it will result in an error. 

Arrays and tables need to be defined in the correct order. Once an array is defined, you can’t mix the structure with normal tables or attempt to append a table to it after defining it.

Conflicting Array and Table Names

A conflict happens when you try to define a normal table using the same name as an existing array, which leads to a parse-time error. Similarly, if you try to turn a normal table into an array, it will also cause a parse-time error.

TOML
# INVALID TOML DOC

[[fruits]]
name = "mango"

[[fruits.varieties]]
name = "alphonso"

# INVALID: This table conflicts with the previous array of tables
[fruits.varieties]
name = "dasheri"

[fruits.physical]
color = "yellow"
shape = "oval"

# INVALID: This array of tables conflicts with the previous table
[[fruits.physical]]
color = "green"

Here,

  • First part ([[fruits]] and [[fruits.varieties]]): You define an array of tables with [[fruits]] and a nested array [[fruits.varieties]] for the different fruit varieties.
  • Error 1: The line [fruits.varieties] tries to redefine fruits.varieties as a regular table. This is a conflict because fruits.varieties was already defined as an array of tables ([[fruits.varieties]]). You can’t redefine it as a table.
  • Error 2: Similarly, the line [[fruits.physical]] tries to define fruits.physical as an array of tables, but it was already defined as a regular table ([fruits.physical]). This causes a conflict.

In TOML, if a structure is defined as an array or a table, you cannot redefine it as the opposite later.

So, 

Rules for Arrays of Tables in TOML:

  • Appending to arrays: You can’t append to a statically defined array ([]). The array must be explicitly defined before you add any tables to it.
  • Conflicts between arrays and tables: You cannot define a table with the same name as an array, nor can you redefine a table as an array, or vice versa.

Inline Tables in an Array (Compact Format)

As we know, inline tables are defined using curly braces {}. However, we can also represent an array of inline tables (a list of objects), which is also valid in TOML. Here is a valid TOML format:

TOML
# Using Inline Tables
points = [
  { x = 1, y = 2, z = 3 },
  { x = 7, y = 8, z = 9 },
  { x = 2, y = 4, z = 8 }
]

Here,

  • points is an array (list).
  • Each { x = ..., y = ..., z = ... } is an inline table (a dictionary-like structure inside a list).
  • This is ideal when you have small data structures.

Now, how do we represent this using TOML’s standard array of tables format? 

Alternatively, we can use TOML’s standard table array syntax ([[points]] for multiple objects):

TOML
# Using Standard Table Array Syntax

[[points]]
x = 1
y = 2
z = 3

[[points]]
x = 7
y = 8
z = 9

[[points]]
x = 2
y = 4
z = 8

Here,

[[points]] defines an array of tables, meaning multiple objects under points.

Each [[points]] entry creates a new table (object) with x, y, and z values.

This format is easier to read when you have a large number of objects.

Both approaches are valid. The inline table format is more compact, while the table array format is more readable for larger datasets.

Conclusion

TOML tables are a powerful way to structure data in configuration files. Whether using standard tables, nested tables, inline tables, or arrays of tables, understanding their usage helps create clean, well-structured, and readable configurations.

By applying these concepts, you can efficiently manage configuration settings in projects, making them easier to read and maintain!

Skill Up: Software & AI Updates!

Receive our latest insights and updates directly to your inbox

Related Posts

error: Content is protected !!