sax-js

repository·main·Indexed 22 days ago

https://github.com/isaacs/sax-js

An evented streaming XML and HTML parser for JavaScript, designed for Node.js environments and compatible with browsers. It provides a lightweight SAX-style parser via `sax.parser()` and a Node.js readable/writable stream via `sax.createStream()`, supporting both strict XML parsing and loose HTML-style parsing.

Tokens
2.8K
Snippets
6
Records
11
Agent score
28%

What's inside sax

  1. Use sax as a Node.js Stream

    main

    For streaming data (e.g., reading from a file or network), use sax.createStream(strict, options). This returns a standard Node.js readable/writable stream that supports .pipe().

    Note on Error Handling: Because the stream is a proper EventEmitter, unhandled errors will throw. You must listen to the error event. To continue parsing after an error, you must clear the error on the internal parser and call resume().

    // stream usage
    // takes the same options as the parser
    var saxStream = require('sax').createStream(strict, options)
    
    saxStream.on('error', function (e) {
      // unhandled errors will throw, since this is a proper node
      // event emitter.
      console.error('error!', e)
      // clear the error
      this._parser.error = null
      this._parser.resume()
    })
    
    saxStream.on('opentag', function (node) {
      // same object as above
    })
    
    // pipe is supported, and it's readable/writable
    // same chunks coming in also go out.
    fs.createReadStream('file.xml')
      .pipe(saxStream)
      .pipe(fs.createWriteStream('file-copy.xml'))
  2. Configure parser options

    main

    When creating a parser or stream, you can pass an opt object to customize behavior. All options default to false.

    OptionTypeDescription
    trimBooleanWhether or not to trim text and comment nodes.
    normalizeBooleanIf true, turns any whitespace into a single space.
    lowercaseBooleanIf true, lowercases tag and attribute names in loose mode.
    xmlnsBooleanEnables namespace support.
    positionBooleanIf false, the parser stops tracking line/col/position.
    strictEntitiesBooleanIf true, only parses predefined XML entities (&, ', >, <, and ").
    unquotedAttributeValuesBooleanAllows unquoted attribute values. Defaults to false when strict is true, true otherwise.
    noscriptBooleanIf true, suppresses the special behavior for <script> tags in non-strict mode.
  3. Initialize the sax parser

    main

    To use the parser directly, import the library and call sax.parser(strict). The strict argument is a boolean: set it to true for XML parsing (enforces well-formedness) or false for loose/HTML-style parsing.

    To handle events, assign callback functions to the corresponding properties on the parser instance (e.g., parser.onopentag).

    var sax = require('./lib/sax'),
      strict = true, // set to false for html-mode
      parser = sax.parser(strict)
    
    parser.onerror = function (e) {
      // an error happened.
    }
    parser.ontext = function (t) {
      // got some text.  t is the string of text.
    }
    parser.onopentag = function (node) {
      // opened a tag.  node has "name" and "attributes"
    }
    parser.onattribute = function (attr) {
      // an attribute.  attr has "name" and "value"
    }
    parser.onend = function () {
      // parser stream is done, and ready to have more stuff written to it.
    }
    
    parser.write('<xml>Hello, <who name="world">world</who>!</xml>').close()
  4. Parser methods

    main

    The parser instance provides the following methods to control the data flow:

    • write(data): Writes bytes onto the stream. Data can be written in multiple chunks.
    • close(): Closes the stream. No more data can be written until the end event is emitted, signaling the buffer has been processed.
    • resume(): Used to gracefully handle errors. After listening to the error event and resolving the issue, call resume() to continue parsing. The parser will not continue while in an error state otherwise.
  5. Configure SAX parser options

    main

    When initializing a parser via sax.parser(strict, opt) or sax.createStream(strict, opt), you can provide an opt object to customize behavior:

    • lowercase: If true, tag names are converted to lowercase. (Also supports lowercasetags).
    • xmlns: If true, enables namespace support.
    • maxEntityCount: Maximum number of entities allowed (default: 512).
    • maxEntityDepth: Maximum depth of entity nesting (default: 4).
    • strictEntities: If true, uses sax.XML_ENTITIES for strict XML entity validation.
    • unquotedAttributeValues: Controls whether unquoted attribute values are allowed (defaults to !strict).
    • position: Boolean to enable/disable tracking of line and column numbers (default: true).
    • noscript: If true (or if strict is true), the parser treats <script> tags differently.
    • trim: (Used in text nodes) whether to trim whitespace.
    • normalize: (Used in text nodes) whether to normalize whitespace sequences to a single space.
  6. Parser events

    main

    Events can be handled by assigning a function to on<eventname> (for the parser instance) or using .on('eventname', callback) (for the stream interface). All events emit a single argument.

    EventArgumentDescription
    errorErrorAn error occurred. The error is stored in parser.error and must be cleared before resuming.
    textstringA text node.
    doctypestringA <!DOCTYPE declaration.
    processinginstructionobjectA <?xml ... ?> instruction. Object has name and body members.
    sgmldeclarationstringAn SGML declaration (e.g., <!ENTITY p>).
    opentagstartobjectEmitted when the tag name is available but before attributes. Object has name and an empty attributes set.
    opentagobjectAn opening tag. Object has name and attributes. If xmlns is enabled, includes ns (with local, prefix, uri).
    closetagstringA closing tag name.
    attributeobjectAn attribute node. Object has name and value. If xmlns is enabled, includes namespace info.
    commentstringA comment node string.
    opencdataundefinedThe opening tag of a <![CDATA[ block.
    cdatastringThe text within a <![CDATA[ block. May fire multiple times for large blocks.
    closecdataundefinedThe closing tag ]]> of a <![CDATA[ block.
    opennamespaceundefinedStart of a new namespace binding (requires xmlns: true).
    closenamespaceundefinedEnd of a namespace binding (requires xmlns: true).
    endundefinedThe closed stream has ended.
    readyundefinedThe parser has reset and is ready for more write() calls.
    noscriptundefinedTriggered by <script> tags in non-strict mode (unless noscript: true is set).
  7. Parser state and members

    main

    The following properties are available on the parser instance at all times:

    • line: Current line number in the XML document.
    • column: Current column number in the XML document.
    • position: Current character position in the XML document.
    • startTagPosition: Position where the current tag starts.
    • closed: Boolean indicating if the parser can be written to. If true, wait for the ready event before writing again.
    • strict: Boolean indicating if the parser is in strict mode.
    • opt: The options object passed into the constructor.
    • tag: The current tag being processed.
  8. Initialize a SAX parser with sax.parser()

    main

    To create a new parser instance, use sax.parser(strict, opt).

    • strict: A boolean. If true, the parser will throw errors on malformed XML/HTML. If false, it attempts to be more lenient.
    • opt: An optional configuration object.

    The parser instance provides methods like .write(chunk), .end(), and .close(), and allows you to register event handlers for various XML tokens.

    const sax = require('sax');
    const parser = sax.parser(true, { 
      lowercase: true, 
      xmlns: true 
    });
    
    parser.onopentag = (node) => {
      console.log('Opened tag:', node.name);
    };
    
    parser.write('<root><child>Hello</child></root>');
    parser.end();
  9. Create a SAX stream with sax.createStream()

    main

    For use in Node.js-like environments where you want to pipe data into a parser, use sax.createStream(strict, opt). This returns a SAXStream which implements the standard Stream interface, making it compatible with .pipe() and other stream utilities.

    • strict: Boolean for strict mode.
    • opt: Configuration object.
    const sax = require('sax');
    const stream = sax.createStream(true);
    
    stream.on('opentag', (node) => {
      console.log('Tag:', node.name);
    });
    
    stream.write('<xml>data</xml>');
    stream.end();
  10. Adjust the maximum buffer length

    main

    The parser uses internal buffers for various components (like textNode, cdata, etc.). To prevent memory exhaustion from extremely large nodes, the parser checks against sax.MAX_BUFFER_LENGTH.

    You can increase this limit globally:

    sax.MAX_BUFFER_LENGTH = 1024 * 1024; // 1MB

    Setting this to Infinity will allow unlimited buffer sizes, but this is not recommended for untrusted input.

    sax.MAX_BUFFER_LENGTH = 64 * 1024;
  11. Reference the available SAX events

    main

    The parser emits several events that you can listen to using .on(event, handler) or by assigning to properties like .onopentag.

    Available event names in sax.EVENTS:

    [
        'text',
        'processinginstruction',
        'sgmldeclaration',
        'doctype',
        'comment',
        'opentagstart',
        'attribute',
        'opentag',
        'closetag',
        'opencdata',
        'cdata',
        'closecdata',
        'error',
        'end',
        'ready',
        'script',
        'opennamespace',
        'closenamespace',
    ]