The data templating language
Jsonnet

Tutorial

This page is a guided tour through the Jsonnet language, from its most basic features to its powerful object model, punctuated with examples drawn from the world of cocktails. These examples are meant to be fun, and although a little contrived, do not restrict our thinking to any one particular application of Jsonnet.

Caveat: Note that Jsonnet unparses JSON in a simple way. In particular, it alphabetically reorders object fields in its output. This is natural and compatible with JSON, since if order is meaningful, an array of pairs should be used instead of an object. Also, unparsing JSON using a canonical ordering of field names makes it possible to use diff to compare outputs. However, the example output on this page has been manually re-ordered in order to allow easier visual comparison to the given input. The whitespace of the output has also been tweaked to make it fit more neatly on the page. So, if you run these examples yourself, the output might be different (but equivalent).

Simple Syntax Improvements

In Jsonnet, unlike JSON, sufficiently simple object fields (the strings to the left of the colon) do not need quotes if their names are valid identifiers (i.e. match the regular expression [a-zA-Z_][a-zA-Z0-9_]*). In JSON, commas are not allowed at the end of arrays / objects, which can make writing JSON painful if you need to reorder an array or delete the last element. In Jsonnet, extra commas are allowed but not required. Jsonnet also allows both C and C++ style comments.

Input (Jsonnet) Output (JSON)
/* bar_menu.1.jsonnet */
{
    cocktails: {
        // Ingredient quantities are in fluid ounces.
        "Tom Collins": {
            ingredients: [
                { kind: "Farmers Gin", qty: 1.5 },
                { kind: "Lemon", qty: 1 },
                { kind: "Simple Syrup", qty: 0.5 },
                { kind: "Soda", qty: 2 },
                { kind: "Angostura", qty: "dash" },
            ],
            garnish: "Maraschino Cherry",
            served: "Tall",
        },
        Manhattan: {
            ingredients: [
                { kind: "Rye", qty: 2.5 },
                { kind: "Sweet Red Vermouth", qty: 1 },
                { kind: "Angostura", qty: "dash" },
            ],
            garnish: "Maraschino Cherry",
            served: "Straight Up",
        },
    },
}

{
    "cocktails": {

        "Tom Collins": {
            "ingredients": [
                { "kind": "Farmers Gin", "qty": 1.5 },
                { "kind": "Lemon", "qty": 1 },
                { "kind": "Simple Syrup", "qty": 0.5 },
                { "kind": "Soda", "qty": 2 },
                { "kind": "Angostura", "qty": "dash" }
            ],
            "garnish": "Maraschino Cherry",
            "served": "Tall"
        },
        "Manhattan": {
            "ingredients": [
                { "kind": "Rye", "qty": 2.5 },
                { "kind": "Sweet Red Vermouth", "qty": 1 }, 
                { "kind": "Angostura", "qty": "dash" }
            ],
            "garnish": "Maraschino Cherry",
            "served": "Straight Up"
        }
    }
}

References

To avoid duplication, one part of the structure can refer to another part. There is a keyword self that refers to the current object, i.e. the object whose braces we are immediately inside. There is also an operator $ (syntax borrowed from JsonPath) that refers to the root object, i.e. the object whose braces we are inside that is most removed from us by nesting. From one of these, we can follow a path to the other part of the structure, which contains the value we want to reference.

In the example below, we re-use the spirit from the Tom Collins cocktail when defining the recipe for a Martini. In the path to the spirit, we need to look up fields and array indexes. In both cases, square brackets are used to specify which element or field is being looked up. For objects, fields that are valid identifiers (e.g. have no spaces) can be traversed using the dot operator which is a more convenient notation. The commented out line shows what the path would look like if the dot operator was not taken advantage of.

The Gin Martini is another name for a Martini, so an alias has been added by using self, which resolves to the closest surrounding object (in this case, the "cocktails" object).

Input (Jsonnet)
// bar_menu.2.jsonnet
{
    cocktails: {
        "Tom Collins": {
            ingredients: [
                { kind: "Farmers Gin", qty: 1.5 },
                { kind: "Lemon", qty: 1 },
                { kind: "Simple Syrup", qty: 0.5 },
                { kind: "Soda", qty: 2 },
                { kind: "Angostura", qty: "dash" },
            ],
            garnish: "Maraschino Cherry",
            served: "Tall",
        },
        Martini: {
            ingredients: [
                {
                    // Evaluate a path to get the first ingredient of the Tom Collins.
                    kind: $.cocktails["Tom Collins"].ingredients[0].kind,
                    // or $["cocktails"]["Tom Collins"]["ingredients"][0]["kind"],
                    qty: 1,
                },
                { kind: "Dry White Vermouth", qty: 1 },
            ],
            garnish: "Olive",
            served: "Straight Up",
        },
        "Gin Martini": self.Martini,
    },
}
Output (JSON)
{
   "cocktails": {
      "Tom Collins": {
         "ingredients": [
            { "kind": "Farmers Gin", "qty": 1.5 },
            { "kind": "Lemon", "qty": 1 },
            { "kind": "Simple Syrup", "qty": 0.5 },
            { "kind": "Soda", "qty": 2 },
            { "kind": "Angostura", "qty": "dash" }
         ],
         "garnish": "Maraschino Cherry",
         "served": "Tall"
      },
      "Martini": {
         "ingredients": [
            { "kind": "Farmers Gin", "qty": 1 },
            { "kind": "Dry White Vermouth", "qty": 1 }
         ],
         "garnish": "Olive",
         "served": "Straight Up"
      },
      "Gin Martini": {
         "ingredients": [
            { "kind": "Farmers Gin", "qty": 1 },
            { "kind": "Dry White Vermouth", "qty": 1 }
         ],
         "garnish": "Olive",
         "served": "Straight Up"
      }
   }
}

Data Operations

Arithmetic and Conditionals

Jsonnet has constructs for manipulating data. There are the usual arithmetic operators on numbers (double precision floating point), booleans, and strings. The + operator also concatenates arrays and strings. On objects, it fuses the two objects preferring the right hand side when fields conflict. The % operator is compatible with Python's % operator and is used for string formatting.

There is an if construct for conditional code. Since Jsonnet is an expression language, the conditional behaves more like the ternary "?:" operator in C, or the "a if b else c" syntax of Python. I.e., it can be embedded in the middle of an expression.

The example below illustrates some of these features. Note that equality returns false if the two values have different types, as is the case in most dynamically typed scripting languages (but not Javascript). Note also the ||| syntax for text blocks. This strips the leading whitespace but preserves the newlines in the string literal. Just like any other string, one can use the string formatting operator %.

Input (Jsonnet) Output (JSON)
// bar_menu.3.jsonnet
{
    foo: 3,
    bar: 2 * self.foo,  // Multiplication.
    baz: "The value " + self.bar + " is "
         + (if self.bar > 5 then "large" else "small") + ".",
    word: "horse",
    fmt1: "The word %s has %d letters."
          % [self.word, std.length(self.word)],
    fmt2: "The word %(wd)s has %(le)d letters.  Go %(wd)s!"
          % { wd: $.word, le: std.length($.word) },
    array: [1, 2, 3] + [4],
    obj: { a: 1, b: 2 } + { b: 3, c: 4 },
    equality: 1 == "1",
    multiline_string: |||
        1
        2
        3
    |||,
    multiline_string_fmt: |||
        foo = %(foo)d
        bar = %(bar)d
    ||| % self,
}
{
   "foo": 3,
   "bar": 6,
   "baz": "The value 6 is large.",
   "word": "horse",
   "fmt1": "The word horse has 5 letters.",
   "fmt2": "The word horse has 5 letters.  Go horse!",
   "array": [1, 2, 3, 4],
   "obj": {"a": 1, "b": 3, "c": 4},
   "equality": false,
   "multiline_string": "1\n2\n3\n",
   "multiline_string_fmt": "foo = 3\nbar = 6\n"
}

Array and Object Comprehension

There are constructs for array and object comprehension, i.e. creating a list or object by processing each element of another list. The expression before the for is evaluated once for each element in the array after the in, but only if the condition after if holds. The syntax matches Python. Also, field names can be computed in regular object definitions as well, as shown on the last line of the following example.

Input (Jsonnet) Output (JSON)
// example_operators.jsonnet
{
    foo: [1, 2, 3],
    bar: [x * x for x in self.foo if x >= 2],
    baz: { ["field" + x]: x for x in self.foo },
    obj: { ["foo" + "bar"]: 3 },
}
{
   "foo": [ 1, 2, 3 ],
   "bar": [ 4, 9 ],
   "baz": {
      "field1": 1,
      "field2": 2,
      "field3": 3
   },
   "obj": { "foobar": 3 }
}

The next example is less contrived. The first cocktail has equal parts of three ingredients so we use an array comprehension to avoid repeating ourselves. For the sake of example, the qty is also computed as 4/3 instead of being given as 1.3333...

After the +, object comprehension is used to create two more cocktails. In this case, the prefix in front of "Screwdriver" and the kind of fruit juice is what differs each time. Note also the use of null, which is a special value that we have inherited from JSON. The + fuses the two objects together to produce a single record for the "cocktails" field.

Input (Jsonnet) Output (JSON)
// bar_menu.5.jsonnet
{
    cocktails: {
        "Bee's Knees": {
            // Construct the ingredients by using 4/3 oz
            // of each element in the given list.
            ingredients: [  // Array comprehension.
                { kind: i, qty: 4 / 3 }
                for i in ["Honey Syrup", "Lemon Juice", "Farmers Gin"]
            ],
            garnish: "Lemon Twist",
            served: "Straight Up",
        },
    } + {  // Object comprehension.
        [sd.name + "Screwdriver"]: {
            ingredients: [
                { kind: "Vodka", qty: 1.5 },
                { kind: sd.fruit, qty: 3 },
            ],
            garnish: null,
            served: "On The Rocks",
        } for sd in [
            { name: "Yellow ", fruit: "Lemonade" },
            { name: "", fruit: "Orange Juice" },
        ]
    },
}
{
   "cocktails": {
      "Bee's Knees": {
         "ingredients": [
            {
               "kind": "Honey Syrup",
               "qty": 1.3333333333333333
            },
            {
               "kind": "Lemon Juice",
               "qty": 1.3333333333333333
            },
            {
               "kind": "Farmers Gin",
               "qty": 1.3333333333333333
            }
         ],
         "garnish": "Lemon Twist",
         "served": "Straight Up"
      },
      "Yellow Screwdriver": {
         "ingredients": [
            { "kind": "Vodka", "qty": 1.5 },
            { "kind": "Lemonade", "qty": 3 }
         ],
         "garnish": null,
         "served": "On The Rocks"
      },
      "Screwdriver": {
         "ingredients": [
            { "kind": "Vodka", "qty": 1.5 },
            { "kind": "Orange Juice", "qty": 3 }
         ],
         "garnish": null,
         "served": "On The Rocks"
      }
   }
}

Modularity and Encapsulation

As the amount of JSON grows, its size makes it harder to manage. Jsonnet has various constructs to help. A file can be broken into parts, as one Jsonnet file can import other Jsonnet files (and therefore other JSON files). Values can be held in local variables and fields, which are only visible within their scopes. Functions can be defined to factor out common descriptions, and error statements can be used to validate inputs. Jsonnet provides a standard library that is implicitly imported and contains useful functions for data manipulation, among other things.

Imports

The first example below factors out some cocktails into a separate file. This may be useful to allow concurrent modifications by different mixologists. The import construct yields the content of the martinis.libsonnet file. The + operator is object concatenation, which combines two objects to form a single object. Note that the Cosmopolitan field is defined in both files, so the one on the right hand side is used. This means that bar_menu.jsonnet has overridden the recipe from martinis.libsonnet with a different recipe (one that uses Cointreau instead of Triple Sec, among other changes).

// martinis.libsonnet
{
    "Vodka Martini": {
        ingredients: [
            { kind: "Vodka", qty: 2 },
            { kind: "Dry White Vermouth", qty: 1 },
        ],
        garnish: "Olive",
        served: "Straight Up",
    },
    Cosmopolitan: {
        ingredients: [
            { kind: "Vodka", qty: 2 },
            { kind: "Triple Sec", qty: 0.5 },
            { kind: "Cranberry Juice", qty: 0.75 },
            { kind: "Lime Juice", qty: 0.5 },
        ],
        garnish: "Orange Peel",
        served: "Straight Up",
    },
}
// bar_menu.6.jsonnet
{
    cocktails: (import "martinis.libsonnet") + {
        Manhattan: {
            ingredients: [
                { kind: "Rye", qty: 2.5 },
                { kind: "Sweet Red Vermouth", qty: 1 },
                { kind: "Angostura", qty: "dash" },
            ],
            garnish: "Maraschino Cherry",
            served: "Straight Up",
        },
        Cosmopolitan: {
            ingredients: [
                { kind: "Vodka", qty: 1.5 },
                { kind: "Cointreau", qty: 1 },
                { kind: "Cranberry Juice", qty: 2 },
                { kind: "Lime Juice", qty: 1 },
            ],
            garnish: "Lime Wheel",
            served: "Straight Up",
        },
    },
}

By convention, Jsonnet code that is intended only for importing has a .libsonnet extension. This is not enforced, it simply allows you to distinguish between the two kinds of files when listing a directory. In fact, for files that are both imported and executed directly, it is better to keep the .jsonnet suffix. A commonly occurring example of that is test cases.

Functions

The next example demonstrates functions (actually, closures). We have a separate file holding a utility function to help with defining cocktails made from equal parts, such as the Bee's Knees and the Negroni. The utility function also checks the number of ingredients and raises an error if the list is empty. This avoids the low level divide by zero error that would be raised when calculating the quantity, thus avoiding the exposure of implementation details.

There is also an identity function defined. This is there to demonstrate that function definitions are really just syntax sugar for closures that are assigned to a field.

// bar_menu_utils.libsonnet
{
    equal_parts(size, ingredients)::
        if std.length(ingredients) == 0 then
            error "No ingredients specified."
        else [
            { kind: i, qty: size / std.length(ingredients) }
            for i in ingredients
        ],
    id:: function(x) x,
}

Finally, you may have noticed that two colons are used instead of one. This marks the field as being hidden, i.e. it will not appear in the JSON output. The field can still be accessed by Jsonnet code, so it is not like the private/protected modifier that some languages have. Hidden fields are convenient for functions, which cannot be manifestated in the JSON output. It has other uses too, as we will see in the later section on object orientation.

Local Variables

Like most languages, Jsonnet has variables, which can be used to factor out expressions. They are referenced using standard static scoping rules. In the following case, the imported object is stored in the variable, which is later referenced to access the equal_parts function. It is also possible to store the import in a field of the root object, and access it with $. In that case, it should be a hidden field (using the :: syntax) in order to avoid appearing in the output of bar_menu.7.jsonnet.

Input Output
// bar_menu.7.jsonnet
local utils = import "bar_menu_utils.libsonnet";
{
    local my_gin = "Farmers Gin",
    cocktails: {
        "Bee's Knees": {
            // Divide 4oz among the 3 ingredients.
            ingredients: utils.equal_parts(4, [
                "Honey Syrup", "Lemon Juice", my_gin]),
            garnish: "Lemon Twist",
            served: "Straight Up",
        },
        Negroni: {
            // Divide 3oz among the 3 ingredients.
            ingredients: utils.equal_parts(3, [
                my_gin, "Sweet Red Vermouth",
                "Campari"]),
            garnish: "Orange Peel",
            served: "On The Rocks",
        },
    },
}
{
   "cocktails" : {
      "Bee's Knees" : {
         "ingredients" : [
            { "kind" : "Honey Syrup", "qty" : 1.3333333333333333 },
            { "kind" : "Lemon Juice", "qty" : 1.3333333333333333 },
            { "kind" : "Farmers Gin", "qty" : 1.3333333333333333 }
         ],
         "garnish" : "Lemon Twist",
         "served" : "Straight Up"
      },
      "Negroni" : {
         "ingredients" : [
            { "kind" : "Farmers Gin", "qty" : 1 },
            { "kind" : "Sweet Red Vermouth", "qty" : 1 },
            { "kind" : "Campari", "qty" : 1 }
         ],
         "garnish" : "Orange Peel",
         "served" : "On The Rocks"
      }
   }
}

Variables can appear anywhere within the program structure; in particular, they can appear inside an object (i.e. alongside field declarations). The my_gin variable is an example of using local inside an object. In this situation they are analogous to "private" fields, as defined in other languages. This is because the initializer of the variable (in this case the string "Farmer's Gin", but in general an arbitrary expression) can access self and super, just like a field. However, unlike with fields, it is not possible for anyone to access the variable except by name, within the object's scope.

In both cases, there is a separator that indicates the end of the variable initializer. If the variable is defined alongside object fields, the separator is a comma in order to match the regular field separator. Otherwise the separator is a semicolon.

The variable can be referenced within its own initializer, which is essential for writing recursive functions.

Variables offer a more general alternative to the $ operator, by allowing the stashing of the self value. This can be useful to "name" an object in the middle of the tree, because the path from $ might be long. $ is actually equivalent to a stashing self in a variable at the outermost object.

Input Output
// bar_menu.8.jsonnet
{
    cocktails: {
        Negroni: {
            local neg = self,
            ingredients: [
                { kind: "Farmers Gin", qty: 1 },
                { kind: "Sweet Red Vermouth",
                  qty: neg.ingredients[0].qty },
                { kind: "Campari",
                  qty: neg.ingredients[0].qty },
            ],
            garnish: "Orange Peel",
            served: "On The Rocks",
        },
    },
}
{
   "cocktails": {
      "Negroni": {
         "ingredients": [
            { "kind": "Farmers Gin", "qty": 1 },
            { "kind": "Sweet Red Vermouth", "qty": 1 },
            { "kind": "Campari", "qty": 1 }
         ],
         "garnish": "Orange Peel",
         "served": "On The Rocks"
      }
   }
}

Stack Traces

Jsonnet provides stack traces when an error is raised. Here is an example, where we provide an empty list of ingredients to the equal_parts function.

Input Output
// no_ingredients.jsonnet
local utils = import "bar_menu_utils.libsonnet";
utils.equal_parts(4, [])
RUNTIME ERROR: No ingredients specified.
	bar_menu_utils.libsonnet:5:13-45	function 
	no_ingredients.jsonnet:3:1-24	

Object-Orientation

Finally, we see how Jsonnet provides the abstraction mechanisms of object oriented programming, to allow the writing of base templates that can be extended for a variety of purposes.

The object concatenation operator + can be used to override fields from one object with another. This is similar to the concept of inheritance in object-oriented languages such as C++ and Java. It can be used to derive variants from a single template. In the following example, a Whiskey Sour with egg white is derived from the original Whiskey Sour.

The super keyword, as in Java, allows access to the object being derived from, i.e. the object on the left hand side of the + operator. In this case, it is being used to fetch the original ingredients of the whiskey sour, so that a 4th ingredient can be added. Removing the super.ingredients + would result in a cocktail containing only egg white.

Input Output
// bar_menu.9.jsonnet
{
    cocktails: {
        "Whiskey Sour": {
            ingredients: [
                { kind: "Bourbon", qty: 1.5 },
                { kind: "Lemon Juice", qty: 1 },
                { kind: "Gomme Syrup", qty: 0.5 },
            ],
            garnish: "Lemon Peel",
            served: "Straight Up",
        },
        "Whiskey Sour With Egg": self["Whiskey Sour"] + {
            ingredients: super.ingredients
                         + [{ kind: "Egg White", qty: 0.5 }],
        },
    },
}
{
   "cocktails": {
      "Whiskey Sour": {
         "ingredients": [
            { "kind": "Bourbon", "qty": 1.5 },
            { "kind": "Lemon Juice", "qty": 1 },
            { "kind": "Gomme Syrup", "qty": 0.5 }
         ],
         "garnish": "Lemon Peel",
         "served": "Straight Up"
      },
      "Whiskey Sour With Egg": {
         "ingredients": [
            { "kind": "Bourbon", "qty": 1.5 },
            { "kind": "Lemon Juice", "qty": 1 },
            { "kind": "Gomme Syrup", "qty": 0.5 },
            { "kind": "Egg White", "qty": 0.5 }
         ],
         "garnish": "Lemon Peel",
         "served": "Straight Up"
      }
   }
}

The key to making Jsonnet object-oriented is that the self keyword be "late bound". This is illustrated in the next example, where an alternative menu is derived from the above. It takes the original menu, and replaces the cocktails object with a new object that is in turn based on the original menu's cocktail object. But this new cocktail object overrides the Whiskey sour, changing Bourbon to Scotch (among other things). The effect of this is not just to replace the whiskey sour but also to change how the self keyword behaves in the original menu. This results in the egg variant now being derived from the new whiskey, because that is now what self["Whiskey Sour"] resolves to. This is classic object-orientation in action, and it is very powerful.

Note also that this example makes use of two syntax sugars (shorthands). The first is that the object concatenation + was omitted. This is allowed when it is followed by an opening brace, so in fact this same simplification could have also been made in all of the previous examples utilizing the object concatenation operator +. The second syntax sugar is the f +: e operator, which is a little like the += operator from other languages. Its behavior is the same as saying f: super.f + e. This works not just for + when used for inheritance, but also for string concatenation, list concatenation, and arithmetic addition, so it could also have been used in the previous example to add the egg white.

Input Output
// bar_menu.10.jsonnet
(import "bar_menu.9.jsonnet") {
    cocktails+: {
        "Whiskey Sour": {
            ingredients: [
                { kind: "Scotch", qty: 1.5 },
                { kind: "Lemon Juice", qty: 0.75 },
            ],
            garnish: "Lemon Peel",
            served: "On The Rocks",
        },
    },
}
{
    cocktails: {
        "Whiskey Sour": {
            ingredients: [
                { kind: "Scotch", qty: 1.5 },
                { kind: "Lemon Juice", qty: 0.75 }, 
            ],
            garnish: "Lemon Peel",
            served: "On The Rocks", 
        },
        "Whiskey Sour With Egg": {
            ingredients: [
                { kind: "Scotch", qty: 1.5 },
                { kind: "Lemon Juice", qty: 0.75 },
                { kind: "Egg White", qty: 0.5 },
            ],
            garnish: "Lemon Peel",
            served: "On The Rocks",
        }
    }
}

The hidden status (i.e. ::) of a field is preserved over inheritance. If you override a field using the : syntax, but the original field was defined with ::, then the new object's field will also be hidden. To make the field visible use three colons (:::). This is illustrated below. The values are all inherited without change, but the use of colons changes the visibility of fields in the case of x and w. The x field is hidden by foo because of the double colons. The y field remains hidden in foo because the single colon inherits the hidden status from Base. The triple colon in z however overrides the hidden status from Base to make the field visible in foo.

Input Output
// hidden_fields.jsonnet
{
    local Base = { w: 1, x: 2, y:: 3, z:: 4 },
    foo: Base { w: super.w, x:: super.x, y: super.y, z::: super.z },
}
{
   "foo": { "w": 1, "z": 4 }
}

Computed and Optional Fields

In order to support objects whose keys are unknown until run-time, Jsonnet has a syntax allowing the field name to be computed, or omitted entirely. The syntax is similar to object comprehension, which is also a kind of computed field.

Input Output
// bar_menu.11.jsonnet
{
    local name = "Gin Fizz",
    local brunch = true,
    cocktails: {
        [name]: {
            ingredients: [
                { kind: "Farmers Gin", qty: 1.5 },
                { kind: "Lemon", qty: 1 },
                { kind: "Simple Syrup", qty: 0.5 },
                { kind: "Soda", qty: 2 },
                { kind: "Angostura", qty: "dash" },
            ],
            garnish: "Maraschino Cherry",
            served: "Tall",
        },

        [if brunch then "Bloody Mary"]: {
            ingredients: [
                { kind: "Vokda", qty: 1.5 },
                { kind: "Tomato Juice", qty: 3 },
                { kind: "Lemon Juice", qty: 1.5 },
                { kind: "Worcestershire Sauce", qty: 0.25 },
                { kind: "Tobasco Sauce", qty: 0.15 },
            ],
            garnish: "Celery salt & pepper",
            served: "Tall",
        },
    },
}
{
   "cocktails": {
      "Gin Fizz": {
         "garnish": "Maraschino Cherry",
         "ingredients": [
            { "kind": "Farmers Gin", "qty": 1.5 },
            { "kind": "Lemon", "qty": 1 },
            { "kind": "Simple Syrup", "qty": 0.5 },
            { "kind": "Soda", "qty": 2 },
            { "kind": "Angostura", "qty": "dash" }
         ],
         "served": "Tall"
      },
      "Bloody Mary": {
         "garnish": "Celery salt & pepper",
         "ingredients": [
            { "kind": "Vokda", "qty": 1.5 },
            { "kind": "Tomato Juice", "qty": 3 },
            { "kind": "Lemon Juice", "qty": 1.5 },
            { "kind": "Worcestershire Sauce", "qty": 0.25 },
            { "kind": "Tobasco Sauce", "qty": 0.14999999999999999 }
         ],
         "served": "Tall"
      }
   }
}