Skip to content

Customizing Writers

Ozgur Ozcitak edited this page Feb 18, 2019 · 5 revisions

The XML writer can be customized by overriding its functions while calling end. For example, the following functions keeps a p node's children together, without indentation or newline characters added:

var builder = require('xmlbuilder');

var newIndent = function(node, options, level) {
  // do not insert space before the opening tag of a p node's child
  // or the closing tag of a p node
  if ((node.parent && node.parent.name === "p" && options.state === builder.writerState.OpenTag) ||
      (node.name === "p" && options.state === builder.writerState.CloseTag)) {
    return ''
  } else {
    // revert to original function
    return this._indent(node, options, level)
  }
}

var newEndline = function(node, options, level) {
  // do not insert newline after the closing tag of a p node's child
  // or the opening tag of a p node
  if ((node.parent && node.parent.name === "p" && options.state === builder.writerState.CloseTag) ||
      (node.name === "p" && options.state === builder.writerState.OpenTag)) {
    return ''
  } else {
    // revert to original function
    return this._endline(node, options, level)
  }
}

builder.create('html')
  .ele('p', { 'style': 'S1' })
    .ele('span', { 'style': 'S1' }).txt('span contents')
  .end({ writer: { indent: newIndent, endline: newEndline }, pretty: true } )

Output:

<?xml version="1.0"?>
<html>
  <p style="S1"><span style="S1">span contents</span></p>
</html>

Writer Functions

Following writer functions can be overriden. The arguments to each function are the node currently being written, an options object containing settings passed to the writer when end was called and the current level (or depth) of the XML tree. options object also contains a user object which can be used to persist user variables between writer functions and a state value which defines the current writer state as follows:

{
  None : 0, // writer state is unknown
  OpenTag : 1, // writer is at an opening tag, e.g. <node>
  InsideTag : 2, // writer is inside an element
  CloseTag : 3 // writer is at a closing tag, e.g. </node>
}

These constants are also exported as a JS object by the module as require('xmlbuilder').writerState.

Inside an overriden writer function, this or options.writer refers to the writer object. The original function can be accessed by prepending an underscore to its name, e.g. _endline or _cdata.

indent: function(node, options, level)

Writes the indentation string for the given level.

endline: function(node, options, level)

Writes the newline string.

attribute: (att, options, level)

Writes an attribute.

cdata: function(node, options, level)

Writes a CDATA node.

comment: function(node, options, level)

Writes a comment node.

declaration: function(node, options, level)

Writes the XML declaration (<?xml version="1.0"?>).

docType: function(node, options, level)

Writes the DocType node and its children. Be careful when overriding this function as this function is also responsible for writing the internal subset of the DTD.

element: function(node, options, level)

Writes an element node. Be careful when overriding this function as this function is also responsible for writing the element attributes and child nodes.

processingInstruction: function(node, options, level)

Writes an processing instruction node.

raw: function(node, options, level)

Writes a raw text node.

text: function(node, options, level)

Writes a text node.

dtdAttList: function(node, options, level)

Writes an attribute node (!ATTLIST) inside the DTD.

dtdElement: function(node, options, level)

Writes an element node (!ELEMENT) inside the DTD.

dtdEntity: function(node, options, level)

Writes an entity node (!ENTITY) inside the DTD.

dtdNotation: function(node, options, level)

Writes a notation node (!NOTATION) inside the DTD.

openNode: function(node, options, level)

Called right after starting writing a node. This function doesn't produce any output, but can be used to alter the state of the writer.

closeNode: function(node, options, level)

Called right before completing writing a node. This function doesn't produce any output, but can be used to alter the state of the writer.

openAttribute: (att, options, level)

Called right after starting writing an attribute. This function doesn't produce any output, but can be used to alter the state of the writer.

closeAttribute: (att, options, level)

Called right before completing writing an attribute. This function doesn't produce any output, but can be used to alter the state of the writer.

Node Objects

Each node or att object passed to the writer function contains a type property which defines the type of a node as follows:

{
  Element : 1,
  Attribute : 2,
  Text : 3,
  CData : 4,
  EntityReference : 5,
  EntityDeclaration : 6,
  ProcessingInstruction : 7,
  Comment : 8,
  Document : 9,
  DocType : 10,
  DocumentFragment : 11,
  NotationDeclaration : 12,
  Declaration : 201,
  Raw : 202,
  AttributeDeclaration : 203,
  ElementDeclaration : 204
}

These constants are also exported as a JS object by the module as require('xmlbuilder').nodeType. Each node also has a parent property which returns its parent node.

For attributes (attribute, openAttribute, closeAttribute), following properties of the att argument are made public:

  • name - attribute name
  • value - attribute value

For text type nodes (cdata, comment, raw, text, openNode, closeNode, indent, endline), following properties of the node argument are made public:

  • value - node value (contents of a text node)

For processing instruction nodes (processingInstruction, openNode, closeNode, indent, endline), following properties of the node argument are made public:

  • target - instruction target
  • value - instruction target value

For element nodes (element, openNode, closeNode, indent, endline), following properties of the node argument are made public:

  • node - element name
  • attribs - an object containing attributes with keys created from attribute names
  • children - an array containing child nodes

For the XML declaration (declaration, openNode, closeNode, indent, endline), following properties of the node argument are made public:

  • version - XML version number string, e.g. 1.0
  • encoding - encoding declaration, e.g. UTF-8
  • standalone - standalone document declaration

For the DTD (docType, openNode, closeNode, indent, endline), following properties of the node argument are made public:

  • pubID - public identifier of the external subset
  • sysID - system identifier of the external subset
  • children - an array containing child nodes

For an attribute node (!ATTLIST) inside the DTD (dtdAttList, openNode, closeNode, indent, endline), following properties of the node argument are made public:

  • elementName the name of the element containing this attribute
  • attributeName attribute name
  • attributeType type of the attribute
  • defaultValueType default value type (either #REQUIRED, #IMPLIED, #FIXED or #DEFAULT)
  • defaultValue default value of the attribute (only used for #FIXED or #DEFAULT)

For an element node (!ELEMENT) inside the DTD (dtdElement, openNode, closeNode, indent, endline), following properties of the node argument are made public:

  • name - element name
  • value - element content (defaults to #PCDATA)

For an entity node (!ENTITY) inside the DTD (dtdEntity, openNode, closeNode, indent, endline), following properties of the node argument are made public:

  • pe - whether this is a parameter entity or a general entity
  • name - the name of the entity
  • value - internal entity value as a string
  • pubID - public identifier for an external entity
  • sysID - system identifier for an external entity
  • nData - notation declaration for an external entity

For a notation node (!NOTATION) inside the DTD (dtdNotation, openNode, closeNode, indent, endline), following properties of the node argument are made public:

  • name - the name of the notation
  • value - an object with external entity details
  • pubID - public identifier
  • sysID - system identifier