Skip to content

Change a tag, a style or an anchor

Use Transaction.ChangePresentation to set or remove a node's tag and keep its value, and Transaction.ReplaceWithPresentation to write a new value and state its tag or style in one command:

err := file.Edit(func(tx *yamldoc.Transaction) error {
    return tx.ChangePresentation(node, yamldoc.Presentation{Tag: yamldoc.RemoveTag()})
})

An ordinary Replace never changes a tag. A write that would need one is refused with ErrTagIntent, and an ordinary write picks the style for you. These two commands are the way to say you mean it.

Replace a value and its tag

tx.ReplaceWithPresentation(node, 3, yamldoc.Presentation{Tag: yamldoc.RemoveTag()})
// value: !!str "3"  →  value: 3

tx.ReplaceWithPresentation(node, 3, yamldoc.Presentation{Tag: yamldoc.SetTag("!!int")})
// value: !!str "3"  →  value: !!int 3

The value is written as an ordinary write would write it, and the tag follows. It works across kinds too:

tx.ReplaceWithPresentation(node, map[string]any{"a": 1}, yamldoc.Presentation{Tag: yamldoc.SetTag("!!map")})
// value: !!str x  →  value: !!map
//                      a: 1

Change only the tag

ChangePresentation leaves the source alone where it can, so a spelling such as 0x10 survives:

tx.ChangePresentation(node, yamldoc.Presentation{Tag: yamldoc.SetTag("!!int")})
// value: 0x10       →  value: !!int 0x10

tx.ChangePresentation(node, yamldoc.Presentation{Tag: yamldoc.RemoveTag()})
// value: !!int 0x10 →  value: 0x10

Removing a tag keeps the typed value, so the style changes when the bare source would mean something else:

tx.ChangePresentation(node, yamldoc.Presentation{Tag: yamldoc.RemoveTag()})
// value: !!str 3    →  value: "3"
// value: !!str true →  value: "true"

How to spell a tag

SetTag takes the tag as it should appear in the source:

Form Example
Core shorthand !!int
Local tag !local
A handle the document declares with %TAG !e!name
Verbatim !<tag:example.com,2026:x>

Spelling counts. !<tag:yaml.org,2002:int> over !!int is a respelling, and it is written. Setting the exact spelling a node already carries changes nothing, and neither does removing a tag from an untagged node.

What gets refused

A refused intent fails with the error below, poisons the transaction, and leaves the file as it was.

You asked for Error
A handle the document doesn't declare, or text that isn't a single tag ErrTag
A tag the value doesn't satisfy: !!int over the string "abc" ErrTag
A ChangePresentation that would change the value: !!int over the string "3" ErrTag
Removing a tag no style can do without: a !local value, or !!binary under a profile that needs it ErrTag
Any tag intent on an alias, which carries no properties ErrAlias

The profile decides what a tag means. A tag it doesn't interpret, such as !local or !!set under Core, is opaque: the document can carry it, but it has no typed value to compare. So ReplaceWithPresentation writes one, and ChangePresentation refuses to add one because the value would change. Under a profile with !!set, a mapping whose values aren't all null is refused.

Choose a scalar's style

UseStyle writes a scalar in the style you name, or refuses:

tx.ChangePresentation(node, yamldoc.Presentation{Style: yamldoc.UseStyle(yamldoc.StyleSingleQuoted)})
// value: abc        →  value: 'abc'

tx.ReplaceWithPresentation(node, "one\ntwo\n", yamldoc.Presentation{Style: yamldoc.UseStyle(yamldoc.StyleLiteral)})
// value: 'old'      →  value: |
//                        one
//                        two

The styles are StylePlain, StyleSingleQuoted, StyleDoubleQuoted, StyleLiteral and StyleFolded. There's no fallback: where an ordinary write would quite happily switch to double quotes, a stated style that can't be written is refused. A comment on the scalar's line stays on it, and a block style goes on the line with its body below. Asking for the style a scalar already has changes nothing, so "\x41" stays spelled that way.

A style and a tag can go together, which is how a plain scalar reads as something it wouldn't on its own:

tx.ReplaceWithPresentation(node, "true", yamldoc.Presentation{
    Tag:   yamldoc.SetTag("!!str"),
    Style: yamldoc.UseStyle(yamldoc.StylePlain),
})
// value: true       →  value: !!str true

Both are honoured or the command is refused, and the tag is applied first.

You asked for Error
Plain or quoted text that would change the value: 3 in quotes, '3' plain ErrPresentation
Text the style can't carry: a line break in plain, a control character in a block ErrPresentation
A block style in a flow collection, or on a key ErrPresentation
Any style but a string's double quotes on a JSON-style string ErrPresentation
A scalar style on a mapping or sequence, or StyleEmpty, StyleFlow, StyleFlowPair, StyleBlock ErrPresentation
Any style intent on an alias ErrAlias

Add, rename or remove an anchor

tx.ChangePresentation(node, yamldoc.Presentation{Anchor: yamldoc.AddAnchor("base")})
// value: x          →  value: &base x

tx.ChangePresentation(node, yamldoc.Presentation{Anchor: yamldoc.RenameAnchor("shared")})
// value: &base x    →  value: &shared x
// copy: *base       →  copy: *shared

tx.ChangePresentation(node, yamldoc.Presentation{Anchor: yamldoc.RemoveAnchor()})
// value: &shared x  →  value: x

A rename rewrites every alias bound to this anchor and no other: an alias after a redefinition of the old name still belongs to the redefinition. An anchor can go with a tag and a style in one command, and is written after the tag.

Adding the name a node already has, renaming to it, or removing an anchor from a node without one changes nothing.

You asked for Error
A name defined anywhere else in the document, before or after the node ErrAnchor
A name the document's YAML version can't spell: empty, a space, , under 1.2 ErrAnchor
AddAnchor over a different anchor, or RenameAnchor with no anchor ErrAnchor
RemoveAnchor while an alias binds to it: expand those first ErrAnchor
Any anchor intent on an alias ErrAlias

Make or expand an alias

ReplaceWithAlias writes an alias over a node, to an earlier node that already has an anchor:

tx.ReplaceWithAlias(second, first)
// first: &shared {a: 1}     first: &shared {a: 1}
// second: {a: 1}         →  second: *shared

ExpandAlias writes the value the alias refers to in its place. The copy is a new value, so it has no anchor at its root and takes none of the original's comments or number spellings. An anchor nested in the copy takes a fresh name, and the aliases in the copy that refer to it follow:

tx.ExpandAlias(value)
// base: &a                  base: &a
//   x: &in 1                  x: &in 1
//   y: *in                    y: *in
// value: *a              →  value:
//                             x: &yaml1 1
//                             y: *yaml1

To remove an anchor that has aliases, expand each alias and then remove it. All of these can share a transaction.

You asked for Error
An alias to a node with no anchor, or in another document ErrAnchor
An alias before its anchor, or with a nearer redefinition of the name between ErrAnchor
An alias to the node itself, to a node inside it, or to a node around it ErrAnchor
An alias over a node with its own anchor or tag, which an alias can't carry ErrAnchor
An alias, or an expansion, as a mapping key ErrAnchor
An alias in a JSON-style document, which has none ErrPresentation
An alias over a node holding an anchor that a later alias needs ErrReference
Expanding a copy with an alias that would bind elsewhere from the new position ErrAnchor
Expanding a value with a tag the profile doesn't interpret ErrUnsupportedType
Expanding a node that isn't an alias ErrAlias