📄 js-yaml/examples/custom_types

File: custom_types.md | Updated: 11/15/2025

Custom Types in JS-YAML

This example demonstrates how to define custom YAML types for JavaScript classes and use them to load and dump YAML documents with custom tags.

Overview

Custom types allow you to:

  • Define how custom JavaScript objects should be serialized to YAML
  • Define how custom YAML tags should be deserialized to JavaScript objects
  • Create custom schemas that extend the default YAML schema

Example Classes

We define two classes: Point and Space.

function Point(x, y, z) {
  this.klass = 'Point';
  this.x     = x;
  this.y     = y;
  this.z     = z;
}

function Space(height, width, points) {
  if (points) {
    if (!points.every(function (point) { return point instanceof Point; })) {
      throw new Error('A non-Point inside a points array!');
    }
  }

  this.klass  = 'Space';
  this.height = height;
  this.width  = width;
  this.points = points;
}

Defining Custom YAML Types

Point Type

var PointYamlType = new yaml.Type('!point', {
  // Loader must parse sequence nodes only for this type (i.e. arrays in JS terminology).
  // Other available kinds are 'scalar' (string) and 'mapping' (object).
  kind: 'sequence',

  // Loader must check if the input object is suitable for this type.
  resolve: function (data) {
    // `data` may be either:
    // - Null in case of an "empty node"
    // - Array since we specified `kind` to 'sequence'
    return data !== null && data.length === 3;
  },

  // If a node is resolved, use it to create a Point instance.
  construct: function (data) {
    return new Point(data[0], data[1], data[2]);
  },

  // Dumper must process instances of Point by rules of this YAML type.
  instanceOf: Point,

  // Dumper must represent Point objects as three-element sequence in YAML.
  represent: function (point) {
    return [ point.x, point.y, point.z ];
  }
});

Space Type

var SpaceYamlType = new yaml.Type('!space', {
  kind: 'mapping',
  construct: function (data) {
    data = data || {}; // in case of empty node
    return new Space(data.height || 0, data.width || 0, data.points || []);
  },
  instanceOf: Space
  // `represent` is omitted here. So, Space objects will be dumped as is.
  // That is regular mapping with three key-value pairs but with !space tag.
});

Creating a Custom Schema

var SPACE_SCHEMA = yaml.DEFAULT_SCHEMA.extend([ SpaceYamlType, PointYamlType ]);

Using the Custom Schema

fs.readFile(path.join(__dirname, 'custom_types.yml'), 'utf8', function (error, data) {
  var loaded;

  if (!error) {
    loaded = yaml.load(data, { schema: SPACE_SCHEMA });
    console.log(util.inspect(loaded, false, 20, true));
  } else {
    console.error(error.stack || error.message || String(error));
  }
});

Example YAML Document

subject: Custom types in JS-YAML
spaces:
- !space
  height: 1000
  width: 1000
  points:
  - !point [ 10, 43, 23 ]
  - !point [ 165, 0, 50 ]
  - !point [ 100, 100, 100 ]

- !space
  height: 64
  width: 128
  points:
  - !point [ 12, 43, 0 ]
  - !point [ 1, 4, 90 ]

- !space # An empty space

Complete Code

'use strict';

var fs   = require('fs');
var path = require('path');
var util = require('util');
var yaml = require('../');

// Let's define a couple of classes.

function Point(x, y, z) {
  this.klass = 'Point';
  this.x     = x;
  this.y     = y;
  this.z     = z;
}

function Space(height, width, points) {
  if (points) {
    if (!points.every(function (point) { return point instanceof Point; })) {
      throw new Error('A non-Point inside a points array!');
    }
  }

  this.klass  = 'Space';
  this.height = height;
  this.width  = width;
  this.points = points;
}

// Then define YAML types to load and dump our Point/Space objects.

var PointYamlType = new yaml.Type('!point', {
  kind: 'sequence',
  resolve: function (data) {
    return data !== null && data.length === 3;
  },
  construct: function (data) {
    return new Point(data[0], data[1], data[2]);
  },
  instanceOf: Point,
  represent: function (point) {
    return [ point.x, point.y, point.z ];
  }
});

var SpaceYamlType = new yaml.Type('!space', {
  kind: 'mapping',
  construct: function (data) {
    data = data || {};
    return new Space(data.height || 0, data.width || 0, data.points || []);
  },
  instanceOf: Space
});

// After our types are defined, it's time to join them into a schema.

var SPACE_SCHEMA = yaml.DEFAULT_SCHEMA.extend([ SpaceYamlType, PointYamlType ]);

// And read a document using that schema.
fs.readFile(path.join(__dirname, 'custom_types.yml'), 'utf8', function (error, data) {
  var loaded;

  if (!error) {
    loaded = yaml.load(data, { schema: SPACE_SCHEMA });
    console.log(util.inspect(loaded, false, 20, true));
  } else {
    console.error(error.stack || error.message || String(error));
  }
});

// There are some exports to play with this example interactively.
module.exports.Point         = Point;
module.exports.Space         = Space;
module.exports.PointYamlType = PointYamlType;
module.exports.SpaceYamlType = SpaceYamlType;
module.exports.SPACE_SCHEMA  = SPACE_SCHEMA;

Key Concepts

  • kind: Specifies the YAML node type ('scalar', 'sequence', or 'mapping')
  • resolve: Validates whether the YAML node matches this type
  • construct: Converts YAML data to a JavaScript object
  • instanceOf: Identifies which JavaScript class this type represents
  • represent: Converts JavaScript objects back to YAML format