An open API service indexing awesome lists of open source software.

https://github.com/nikku/saxen

A tiny, super fast, namespace aware, sax-style XML parser.
https://github.com/nikku/saxen

javascript sax sax-parser xml-parser

Last synced: 9 days ago
JSON representation

A tiny, super fast, namespace aware, sax-style XML parser.

Awesome Lists containing this project

README

          

# `/saxen/` parser

[![CI](https://github.com/nikku/saxen/workflows/CI/badge.svg)](https://github.com/nikku/saxen/actions?query=workflow%3ACI)
[![Codecov](https://img.shields.io/codecov/c/github/nikku/saxen.svg)](https://codecov.io/gh/nikku/saxen)

A tiny, super fast, namespace aware [sax-style](https://en.wikipedia.org/wiki/Simple_API_for_XML) XML parser written in plain JavaScript.

## Features

* (optional) entity decoding and attribute parsing
* (optional) namespace aware
* element / attribute normalization in namespaced mode
* tiny (`2.6Kb` minified + gzipped)
* [pretty damn fast](https://github.com/nikku/js-sax-parser-tests)

## Usage

```javascript
import {
Parser
} from 'saxen';

const parser = new Parser();

// enable namespace parsing: element prefixes will
// automatically adjusted to the ones configured here
// elements in other namespaces will still be processed
parser.ns({
'http://foo': 'foo',
'http://bar': 'bar'
});

parser.on('openTag', function(elementName, attrGetter, decodeEntities, selfClosing, getContext) {

elementName;
// with prefix, i.e. foo:blub

const attrs = attrGetter();
// { 'bar:aa': 'A', ... }
});

parser.parse('');
```

## Supported Hooks

We support the following parse hooks:

* `openTag(elementName, attrGetter, decodeEntities, selfClosing, contextGetter)`
* `closeTag(elementName, decodeEntities, selfClosing, contextGetter)`
* `error(err, contextGetter)`
* `warn(warning, contextGetter)`
* `text(value, decodeEntities, contextGetter)`
* `cdata(value, contextGetter)`
* `comment(value, decodeEntities, contextGetter)`
* `attention(str, decodeEntities, contextGetter)`
* `question(str, contextGetter)`

In contrast to `error`, `warn` receives recoverable errors, such as malformed attributes.

In [proxy mode](#proxy-mode), `openTag` and `closeTag` a view of the current element replaces the raw element name. In addition element attributes are not passed as a getter to `openTag`. Instead, they get exposed via the `element.attrs`:

* `openTag(element, decodeEntities, selfClosing, contextGetter)`
* `closeTag(element, selfClosing, contextGetter)`

## Namespace Handling

In namespace mode, the parser will adjust tag and attribute namespace prefixes before
passing the elements name to `openTag` or `closeTag`. To do that, you need to
configure default prefixes for wellknown namespaces:

```javascript
parser.ns({
'http://foo': 'foo',
'http://bar': 'bar'
});
```

To skip the adjustment and still process namespace information:

```javascript
parser.ns();
```

## Proxy Mode

In this mode, the first argument passed to `openTag` and `closeTag` is an object that exposes more internal XML parse state. This needs to be explicity enabled by instantiating the parser with `{ proxy: true }`.

```javascript
// instantiate parser with proxy=true
const parser = new Parser({ proxy: true });

parser.ns({
'http://foo-ns': 'foo'
});

parser.on('openTag', function(el, decodeEntities, selfClosing, getContext) {
el.originalName; // root
el.name; // foo:root
el.attrs; // { 'xmlns:foo': ..., id: '1' }
el.ns; // { xmlns: 'foo', foo: 'foo', foo$uri: 'http://foo-ns' }
});

parser.parse('')
```

Proxy mode comes with a performance penelty of roughly five percent.

__Caution!__ For performance reasons the exposed element is a simple view into the current parser state. Because of that, it will change with the parser advancing and cannot be cached. If you would like to retain a persistent copy of the values, create a shallow clone:

```javascript
parser.on('openTag', function(el) {
const copy = Object.assign({}, el);
// copy, ready to keep around
});
```

## Streaming Mode

Instead of parsing a complete document via `parse`, you may feed the parser XML in chunks via `write` and signal the end of the stream via `end`. This allows you to process huge documents in a memory efficient, step by step manner:

```javascript
const parser = new Parser();

parser.on('openTag', function(name) { /* ... */ });

parser
.write('')
.write('')
.write('')
.end();
```

Chunks may split anywhere, even in the middle of a tag, attribute, comment or CDATA section; the parser buffers the incomplete remainder until the next `write`. Parse events are emitted as soon as the corresponding token is complete. Calling `end` flushes the stream and reports an error (via the `error` hook and as the return value) if the buffered remainder is incomplete.

The `write` / `end` pair mirrors Node's [writable stream](https://nodejs.org/api/stream.html#class-streamwritable) contract, making it straightforward to wire the parser up as a stream sink.

__Note:__ In streaming mode the parse context (`line` / `column`) is relative to the currently buffered input rather than the whole document.

## Non-Features

`/saxen/` lacks some features known in other XML parsers such as [sax-js](https://github.com/isaacs/sax-js):

* no support for parsing loose documents, such as arbitrary HTML snippets
* no support for text trimming
* no automatic entity decoding
* no automatic attribute parsing

...and that is ok ❤.

## Credits

We build on the awesome work done by [easysax](https://github.com/vflash/easysax).

`/saxen/` is named after [Sachsen](https://en.wikipedia.org/wiki/Saxony), a federal state of Germany. So geht sächsisch!

## LICENSE

MIT