DEV Community

Hercules Lemke Merscher
Hercules Lemke Merscher

Posted on • Originally published at bitmaybewise.substack.com

The Sneaky String Concatenation Of Jsonnet

Have you ever been bitten by a weird language quirk? Well, I stumbled upon something interesting while working with Jsonnet (that config language that makes our lives easier... mostly).

At first glance, this looks like it should throw an error, right?

"2 apples + " + 42.0
+ ", " { answer: 42 }
+ ", " + [42]
Enter fullscreen mode Exit fullscreen mode

This code actually works, despite the lack of a plus operator on the line where a string precedes an object:

$ jsonnet concat_string_to_multiple_types.jsonnet
"2 apples + 42, {\"answer\": 42}, [42]"
Enter fullscreen mode Exit fullscreen mode

It is happening because Jsonnet is automatically converting objects to strings and implicitly concatenating them 🤯

This seems an undesirable effect, but it might not be that unexpected. Let's dig in!

Jsonnet also uses the plus operator for merging objects, e.g.:

{ a: 1 } + { b: 2 }
Enter fullscreen mode Exit fullscreen mode

And the output will be:

$ jsonnet merge_objects.jsonnet
{
   "a": 1,
   "b": 2
}
Enter fullscreen mode Exit fullscreen mode

Looking at go-jsonnet parser, it returns a new ast.ApplyBrace value when it finds a left brace token:

case tokenBraceL:
                obj, end, err := p.parseObjectRemainder(op)
                if err != nil {
                    return nil, err
                }
                lhs = &ast.ApplyBrace{
                    NodeBase: ast.NewNodeBaseLoc(locFromTokens(begin, end), ast.Fodder{}),
                    Left:     lhs,
                    Right:    obj,
                }
Enter fullscreen mode Exit fullscreen mode

It holds the left and right-hand side values. Let's see the ast type definition:

// ApplyBrace represents e { }.  Desugared to e + { }.
type ApplyBrace struct {
    Left  Node
    Right Node
    NodeBase
}
Enter fullscreen mode Exit fullscreen mode

Huh!? Turns out, it's not an unexpected behavior.

It's a syntax sugar, where we can omit the plus operator:

{ a: 1 } { b: 2 }
Enter fullscreen mode Exit fullscreen mode

The output will be the same. It's a very common pattern used in Jsonnet. It might not look like so with the contrived example I presented above, but it's normal to see examples such as:

local book = { title: "hitchhiker's guide to the galaxy" };
// a bunch more code...
book {
    answer: 42
}
Enter fullscreen mode Exit fullscreen mode

That outputs:

$ jsonnet merge_objects.jsonnet
{
   "answer": 42,
   "title": "hitchhiker's guide to the galaxy"
}
Enter fullscreen mode Exit fullscreen mode

The thing is if either the left or right-hand side of ast.ApplyBrace is a string, the plus operator will be applied and when you try to add anything to a string, the value will be cast to string automatically to be concatenated.

As far as I know, this behavio is not documented, but is part of the language spec. I assume this is on purpose, as I doubt anyone would like users trying to rely on such a tricky aspect of the language.

So next time you're concatenating strings in Jsonnet, double-check those plus signs -- or your objects might just string themselves along! 😅


Thanks for reading Bit Maybe Wise! Subscribe for more Jsonnet-related posts in the future.

Top comments (0)