Tags and explicit types

YAML · Expert · 12 min

Tags explain both the explicit-typing escape hatch and the custom syntax many YAML-based tools invent.

Every node in YAML has a tag that identifies its type. Usually the tag is resolved implicitly: plain 42 gets tag:yaml.org,2002:int. You can set it explicitly:

  • Standard tags with the !! shorthand for tag:yaml.org,2002:: !!str 123 is the string "123", !!float 1 is 1.0, !!int "7", !!null, !!bool. Also !!map, !!seq, and in YAML 1.1 !!binary, !!timestamp, !!set, !!omap.
  • Local tags start with a single ! and mean whatever the application decides. They are how tools extend YAML:
  • AWS CloudFormation: !Ref MyBucket, !Sub "arn:aws:s3:::${Bucket}/*", !GetAtt.
  • Home Assistant: !secret api_key, !include automations.yaml.
  • GitLab CI: !reference [.setup, script].

A generic parser that doesn't know a local tag either errors or returns a placeholder. Loading a CloudFormation template with plain yaml.safe_load fails on !Ref; you have to register a constructor for each tag (or use the tool's own library such as cfn-flip).

Registering constructors is also how you map tags to your own types in code, the safe counterpart of the !!python/... tags: you choose exactly which tags are allowed and what they build.

%TAG directives can define your own prefixes, but they are rarely used outside the spec.

Example

import yaml

print(yaml.safe_load("a: !!str 123\nb: !!float 1\nc: 123"))
# {'a': '123', 'b': 1.0, 'c': 123}

template = '''
Resources:
  Bucket:
    Type: AWS::S3::Bucket
Outputs:
  Name:
    Value: !Ref Bucket
'''
class Ref(str): pass

class CfnLoader(yaml.SafeLoader): pass
CfnLoader.add_constructor("!Ref", lambda loader, node: Ref(loader.construct_scalar(node)))

data = yaml.load(template, Loader=CfnLoader)
v = data["Outputs"]["Name"]["Value"]
print(type(v).__name__, v)          # Ref Bucket
# yaml.safe_load(template) -> ConstructorError: could not determine a constructor for the tag '!Ref'

Remember

  • !!str, !!int, !!float force standard types
  • Single-! local tags are application-defined (CloudFormation !Ref, Home Assistant !secret)
  • Register a constructor per local tag on a safe loader to parse such files

Watch out for

  • Parsing a CloudFormation or Home Assistant file with a generic loader and getting unknown-tag errors.

Check yourself

  1. What does !!str 0123 produce?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: yaml.org/spec/1.2.2/#24-tags