js-yaml - v5.3.0
    Preparing search index...

    Custom tags

    Use defineSequenceTag() when a value is represented by positional items, and defineMappingTag() when it is represented by named fields.

    import { CORE_SCHEMA, defineMappingTag, defineSequenceTag, dump, load } from 'js-yaml'

    class Point {
    constructor (x = 0, y = 0, z = 0) {
    this.x = x; this.y = y; this.z = z
    }
    }

    class Space {
    constructor (height = 0, width = 0, points = []) {
    this.height = height; this.width = width; this.points = points
    }
    }

    const schema = CORE_SCHEMA.withTags(
    defineSequenceTag('!point', {
    create: () => new Point(),
    addItem: (point, value, index) => {
    if (index === 0) point.x = value
    else if (index === 1) point.y = value
    else if (index === 2) point.z = value
    else throw new Error('!point expects exactly 3 items')
    },
    identify: value => value instanceof Point,
    represent: point => [point.x, point.y, point.z]
    }),

    defineMappingTag('!space', {
    create: () => new Space(),
    addPair: (space, key, value) => {
    if (key === 'height') space.height = value
    else if (key === 'width') space.width = value
    else if (key === 'points') space.points = value
    return ''
    },
    has: () => false,
    keys: space => Object.keys(space),
    get: (space, key) => space[key],
    identify: value => value instanceof Space,
    represent: space => new Map([
    ['height', space.height],
    ['width', space.width],
    ['points', space.points]
    ])
    })
    )

    // Load and dump custom tags.
    const source = `
    spaces:
    - !space
    height: 1000
    width: 1000
    points:
    - !point [10, 43, 23]
    - !point [165, 0, 50]
    `

    try {
    const value = load(source, { schema })

    value.spaces[0] instanceof Space // true
    value.spaces[0].points[0] instanceof Point // true

    console.log(dump(value, { schema, flowLevel: 3 }))
    } catch (e) {
    console.error(e)
    }

    Output:

    spaces:
    - !space
    height: 1000
    width: 1000
    points: [!point [10, 43, 23], !point [165, 0, 50]]

    To produce immutable values, build them in two stages. First, collect their contents in a mutable carrier. Then use finalize to create the actual object from the completed carrier:

    import { CORE_SCHEMA, defineSequenceTag, load } from 'js-yaml'

    class ImmutablePoint {
    constructor (coordinates) {
    this.coordinates = Object.freeze([...coordinates]); Object.freeze(this)
    }
    }

    const schema = CORE_SCHEMA.withTags(defineSequenceTag('!point', {
    create: () => [],
    addItem: (carrier, item) => {
    if (typeof item !== 'number') return '!point coordinates must be numbers'
    carrier.push(item)
    },
    finalize: carrier => {
    if (carrier.length !== 2) throw new Error('!point expects exactly 2 coordinates')
    return new ImmutablePoint(carrier)
    },
    identify: value => value instanceof ImmutablePoint,
    represent: point => point.coordinates
    }))

    try {
    const point = load('!point [10, 20]', { schema })
    console.log(point)
    } catch (e) {
    console.error(e)
    }

    Output:

    ImmutablePoint { coordinates: [ 10, 20 ] }
    

    If tag names are not known in advance, set matchByTagPrefix on one definition for each YAML node kind. Use representTagName to preserve the matched tag name when dumping.

    import { CORE_SCHEMA, defineMappingTag, defineScalarTag, defineSequenceTag, dump, load } from 'js-yaml'

    class TaggedValue {
    constructor (tagName, nodeKind, value) {
    this.tagName = tagName; this.nodeKind = nodeKind; this.value = value
    }
    }

    const schema = CORE_SCHEMA.withTags(
    defineScalarTag('!', {
    matchByTagPrefix: true,
    resolve: (source, _isExplicit, tagName) => new TaggedValue(tagName, 'scalar', source),
    identify: value => value instanceof TaggedValue && value.nodeKind === 'scalar',
    representTagName: value => value.tagName,
    represent: value => value.value
    }),

    defineSequenceTag('!', {
    matchByTagPrefix: true,
    create: tagName => new TaggedValue(tagName, 'sequence', []),
    addItem: (tagged, item) => { tagged.value.push(item) },
    identify: value => value instanceof TaggedValue && value.nodeKind === 'sequence',
    representTagName: value => value.tagName,
    represent: value => value.value
    }),

    defineMappingTag('!', {
    matchByTagPrefix: true,
    create: tagName => new TaggedValue(tagName, 'mapping', new Map()),
    addPair: (tagged, key, value) => {
    tagged.value.set(key, value)
    return ''
    },
    has: (tagged, key) => tagged.value.has(key),
    keys: tagged => tagged.value.keys(),
    get: (tagged, key) => tagged.value.get(key),
    identify: value => value instanceof TaggedValue && value.nodeKind === 'mapping',
    representTagName: value => value.tagName,
    represent: value => value.value
    })
    )

    // Load and dump arbitrary tags.
    const source = `
    scalar: !unknown_scalar_tag foo bar
    sequence: !unknown_sequence_tag [1, 2, 3]
    mapping: !unknown_mapping_tag { foo: 1, bar: 2 }
    `

    try {
    const value = load(source, { schema })
    console.log(dump(value, { schema, flowLevel: 1 }))
    } catch (e) {
    console.error(e)
    }

    Output:

    scalar: !unknown_scalar_tag foo bar
    sequence: !unknown_sequence_tag [1, 2, 3]
    mapping: !unknown_mapping_tag {foo: 1, bar: 2}