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 |