# hugml

> An XML parsing and serializing library based on Google's GDATA and BadgerFish conventions. Supports namespaces.

Latest version **1.2.0** (published 2020-08-11) · 0 weekly downloads

## Install

```sh
npm install hugml
pnpm add hugml
yarn add hugml
bun add hugml
```

Provides the command `hugml`.

## Health

**Score 15/100 (F)** — status: abandoned.

Positive: no vulnerabilities.

Warnings: low downloads; no types; no esm support.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 1.2.0 |
| Published | 2020-08-11 |
| First published | 2017-09-02 |
| Weekly downloads | 0 |
| TypeScript types | none |
| Module format | CommonJS |
| Dependencies | 6 |
| Unpacked size | 57.8 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| GitHub stars | 9 |
| Author | Andri Möll |
| Maintainers | moll |
| Keywords | xml, gdata, badgerfish, c14n, canonicalize, canonicalization |

## Links

- npm: https://www.npmjs.com/package/hugml
- Repository: https://github.com/moll/js-hugml
- Issues: https://github.com/moll/js-hugml/issues
- npm.io page: https://npm.io/package/hugml

## Dependencies (6)

- [sax](https://npm.io/package/sax.md) >= 1.2.1 < 2
- [neodoc](https://npm.io/package/neodoc.md) >= 1.3 < 3
- [oolong](https://npm.io/package/oolong.md) ^1.15
- [lodash.uniq](https://npm.io/package/lodash.uniq.md) >= 2 < 4
- [concat-stream](https://npm.io/package/concat-stream.md) ^1.4.0
- [lodash.difference](https://npm.io/package/lodash.difference.md) >= 2 < 4

## Alternatives

- [babylon](https://npm.io/package/babylon.md) — 5.1M weekly downloads
- [csscolorparser](https://npm.io/package/csscolorparser.md) — 3.7M weekly downloads
- [expr-eval-fork](https://npm.io/package/expr-eval-fork.md) — 1.5M weekly downloads
- [@leeoniya/ufuzzy](https://npm.io/package/@leeoniya/ufuzzy.md) — 247.7K weekly downloads
- [xml-parser](https://npm.io/package/xml-parser.md) — 78.4K weekly downloads

## Recent versions

- 1.2.0 (latest) — 2020-08-11
- 1.1.1 — 2019-11-25
- 1.1.0 — 2019-11-14
- 1.0.1 — 2018-05-04
- 1.0.0 — 2017-09-02

## README

HugML.js
========
[![NPM version][npm-badge]](https://www.npmjs.com/package/hugml)
[![Build status][build-badge]](https://github.com/moll/js-hugml/actions/workflows/node.yaml)

HugML.js is an XML parsing and serializing/stringifying library for JavaScript based on **[Google's GData][gdata]** and **[BadgerFish][badgerfish]** conversion conventions. It **supports namespaces** and **namespace aliasing** to make working with more complex XML convenient. The *ML* at the end of HugML stands for "Markup Language" — a markup language of angled hugs (`<<<>>>`). :)

[npm-badge]: https://img.shields.io/npm/v/hugml.svg
[build-badge]: https://github.com/moll/js-hugml/actions/workflows/node.yaml/badge.svg
[gdata]: https://developers.google.com/gdata/docs/json?csw=1
[badgerfish]: http://badgerfish.ning.com

### Tour
See below for a description of the entire format, but for a quick example, take the following XML:

```xml
<?xml version="1.0" encoding="UTF-8" ?>
<people xmlns="urn:example:people" xmlns:ns0="urn:example:properties">
  <person sex="male">
    <ns0:name>John</ns0:name>
    <ns0:age>13</ns0:age>
  </person>

  <person sex="female">
    <ns0:name>Mary</ns0:name>
    <ns0:age>42</ns0:age>
  </person>
</people>
```

With proper namespace aliasing configuration, you can get back the following plain object, regardless of what namespace aliases the original XML used. That's especially useful if the XML input is out of your control and you can't depend on it always having the same aliases.

```json
{
  "version": "1.0",
  "encoding": "UTF-8",

  "people": {
    "xmlns": "urn:example:people",
    "xmlns:ns0": "urn:example:properties",

    "person": [{
      "sex": "male",
      "props$name": {"$": "John"},
      "props$age": {"$": "13"}
    }, {
      "sex": "female",
      "props$name": {"$": "Mary"},
      "props$age": {"$": "42"}
    }]
  }
}
```


Installing
----------
```sh
npm install hugml
```

HugML.js follows [semantic versioning](http://semver.org), so feel free to depend on its major version with something like `>= 1.0.0 < 2` (a.k.a `^1.0.0`).


Using
-----
```javascript
var Hugml = require("hugml")

Hugml.parse(xml) // => Returns a plain object in the format described below.
Hugml.stringify(obj)
```

### Namespaces
The export from `require("hugml")` has both static methods shown above and can be invoked as a constructor to configure namespaces and get back an instance of `Hugml`.

Using the XML example above, you could set the `people` namespace to be the default and `properties` to be renamed as `props`:

```javascript
var Hugml = require("hugml")

var hugml = new Hugml({
  "urn:example:people": "",
  "urn:example:properties": "props"
})

hugml.parse(`
  <people xmlns="urn:example:people" xmlns:ns0="urn:example:properties">
    <person sex="female">
      <ns0:name>Mary</ns0:name>
      <ns0:age>42</ns0:age>
    </person>
  </people>
`)
```

Tags in the `urn:example:people` namespace will end up as unqualified properties (accessed `obj.people.person`) and tags in the `urn:example:properties` namespace will get the `props$` prefix. Values of tag attributes will still end up as string values of properties and the textual content of a tag will end up in a property called `$`.

```json
{
  "people": {
    "xmlns": "urn:example:people",
    "xmlns:ns0": "urn:example:properties",

    "person": {
      "sex": "female",
      "props$name": {"$": "Mary"},
      "props$age": {"$": "42"}
    }
  }
}
```

To rename the default XML namespace to something else, use `""` as the namespace URI:

```javascript
var hugml = new Hugml({"": "h"})

hugml.parse(`
  <html>
    <head>
      <title>Page</title>
    </head>
  </html>
`)
```

```json
{
  "h$html": {
    "h$head": {
      "h$title": {"$": "Page"}
    }
  }
}
```

For serializing a plain object back to XML, just pass it in the same format as above to `Hugml.prototype.stringify`. You don't need the namespace `xmlns` attributes. They'll be added automatically based on the namespaces you've configured and the ones you've actually used.

```js
var hugml = new Hugml({"urn:example:people": ""})
hugml.stringify({"people": {"person": {}}})
```

### Canonicalization
HugML.js has preliminary support for [Exclusive XML Canonicalization](https://www.w3.org/TR/xml-exc-c14n). It may not be fully compliant yet and doesn't play well with parsed XML, but is enough to generate valid hashes of new XML generated by HugML.js for [XML Digital Signatures](https://www.w3.org/TR/xmldsig-core/).

To serialize canonicalized XML from a plain object, use the `canonicalize` method:

```js
var hugml = new Hugml({"urn:example:people": "people"})
hugml.canonicalize({"collection": {"people$person": {}}})
```

This will then return a string following the exclusive canonicalization rules, such as minimally scoped namespaces, sorted attributes and lack of self-closing tags.

```xml
<collection>
  <people:person xmlns:people="urn:example:people"></people:person>
</collection>
```

Occasionally you may want to canonicalize (and later hash and sign) only a subset of an XML document. Because canonicalization preserves XML tag indentation, you can't just extract that part of the object and pass it separately to the `canonicalize` function. You'll need to pass the entire object and a path to the node you wish to be canonicalized:

```js
var hugml = new Hugml({"urn:example:people": "people"})
var obj = {"collection": {"people$person": {name: {$: "John"}}}}
hugml.canonicalize(obj, ["collection", "people$person"])
```

This will return the canonicalized `<people:person>` node with its closing tag properly indented:

```xml
<people:person xmlns:people="urn:example:people">
    <name>John</name>
  </people:person>
```


CLI
---
There's a `hugml` executable installed along with the library. You can use that to do quick tests.

If you've installed HugML.js globally, invoke `hugml`. If you've installed it as a module in the current directory, you'll find `hugml` in `node_modules/.bin/hugml`.

Give the executable an XML file to process. If you leave it out, it'll get the XML from stdin. It'll print the output to stdout.

```sh
hugml foo.xml
curl http://example.com/foo.xml | hugml > foo.json
```

I used the executable to generate examples for this README by copying some XML to the clipboard and then used MacOS's `pbpaste` to pass it on:

```sh
pbpaste | hugml
```

### CLI namespaces
You can also set up namespace aliases for the CLI. Use the `--namespace` argument:

```sh
hugml --namespace http://www.w3.org/2001/XMLSchema=schema <<end
<?xml version="1.0" encoding="ISO-8859-1" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"></xs:schema>
end
```

It'll then print the following to stdout:
```json
{
  "version": "1.0",
  "encoding": "ISO-8859-1",
  "schema$schema": {
    "xmlns:xs": "http://www.w3.org/2001/XMLSchema"
  }
}
```

Format
------
The general algorithm for converting between XML and JSON is as follows:

1. All **XML** is returned as an object with the XML pragma's attributes as properties of it:
   ```xml
   <?xml version="1.0" encoding="UTF-8" ?>
   ```

   Parses to:
   ```json
   {"version": "1.0", "encoding": "UTF-8"}
   ```

   Root tags will be added to that object as properties following the same rules as nested tags.

2. **Tags** will be converted to properties with the tag name as key and an object as value:
   ```xml
   <people>
     <person />
   </people>
   ```

   Parses to:
   ```json
   {"people": {"person": {}}}
   ```

3. **Tags in namespaces** will retain their colon-separated name if it's an unknown alias:
   ```xml
   <p:people xmlns:p="urn:example:people">
     <p:person />
   </p:people>
   ```

   Parses to:
   ```json
   {"p:people": {"p:person": {}}}
   ```

   If it's a renamed alias (see above for further details), its renamed alias will be concatenated with "$" and its local name:
   ```js
   var hugml = new Hugml({"urn:example:people": "peeps"})
   ```

   ```xml
   <p:people xmlns:p="urn:example:people">
     <p:person />
   </p:people>
   ```

   Parses to:
   ```json
   {"peeps$people": {"peeps$person": {}}}
   ```

3. **Tag attributes** will be converted to string properties of the above object value:
   ```xml
   <person id="42" sex="male" />
   ```

   Parses to:
   ```json
   {
     "person": {
       "id": "42",
       "sex": "male"
     }
   }
   ```

4. **Tag textual content** will be added as another property with the `$` name:
   ```xml
   <html>
     <head title="Collides with title tag">
       <title>Page</title>
     </head>
   </html>
   ```

   Parses to:
   ```json
   {
    "html": {
      "head": {
        "title": {"$": "Page"}
      }
    }
   }
   ```

5. **Nested tags** will be properties just like attributes, but following step 2, will have object values.

   Note that because attributes and tags go on the same object, tags may collide with and shadow attributes. In practice that happens rarely and the convenience outweighs the risk. However, if it does affect you, you can work-around it by configuring a namespace:

   ```js
   new Hugml({"": "h"}).parse(`
     <html>
       <head title="Collides with title tag">
         <title>Page</title>
       </head>
     </html>
   `)
   ```

   ```json
   {
     "h$html": {
       "h$head": {
         "title": "Collides with title tag",
         "h$title": {"$:" "Page"}
       }
     }
   }
   ```

The serializing format you give to `Hugml.prototype.stringify` matches the above and is applied in reverse.


License
-------
HugML.js is released under a *Lesser GNU Affero General Public License*, which in summary means:

- You **can** use this program for **no cost**.
- You **can** use this program for **both personal and commercial reasons**.
- You **do not have to share your own program's code** which uses this program.
- You **have to share modifications** (e.g. bug-fixes) you've made to this program.

For more convoluted language, see the `LICENSE` file.


About
-----
**[Andri Möll][moll]** typed this and the code.  
[Monday Calendar][monday] supported the engineering work.

If you find HugML.js needs improving, please don't hesitate to type to me now at [andri@dot.ee][email] or [create an issue online][issues].

[email]: mailto:andri@dot.ee
[issues]: https://github.com/moll/js-hugml/issues
[moll]: https://m811.com
[monday]: https://mondayapp.com

---
_Source: https://npm.io/package/hugml · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
