Skip to content

tuananh/camaro

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

751 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

camaro

camaro is a Node.js library that transform XML to JSON, using Node.js bindings to a native XML parser pugixml - one of the fastest XML parsers around.

npm Build status npm license

🀘 Features

  • Transform XML to JSON: transform only properties you're interested in. Output is ready to use JS object.

  • Written in C++ and compiled down to WebAssembly, so no re-compilation needed.

  • Works on all major platforms (Linux, macOS, Windows).

  • It's pretty fast on large XML strings. We leverage pugixml through our own fork.

  • Scale well with multi-core processors by the use of worker_threads pool.

πŸ”₯ Benchmark

benchmark                   avg (min … max) p75 / p99    (min … top 1%)
------------------------------------------- -------------------------------
camaro v6                    182.58 Β΅s/iter 183.62 Β΅s   β–ˆ                  
                    (160.51 Β΅s … 840.49 Β΅s) 280.90 Β΅s   β–ˆβ–ƒ                 
                    (328.00  b …   1.23 mb)  14.87 kb β–‚β–‡β–ˆβ–ˆβ–†β–„β–ƒβ–ƒβ–‚β–‚β–‚β–β–β–β–β–β–β–β–β–β–

txml                         562.42 Β΅s/iter 522.79 Β΅s β–ˆ                    
                      (515.79 Β΅s … 1.25 ms)   1.04 ms β–ˆ                    
                    (164.28 kb …   1.75 mb) 308.49 kb β–ˆβ–β–β–β–β–β–ƒβ–β–β–β–β–β–β–β–β–‚β–β–β–β–β–

fast-xml-parser                2.65 ms/iter   2.63 ms  β–ƒβ–…β–ˆ                 
                        (2.32 ms … 4.27 ms)   4.07 ms β–‚β–ˆβ–ˆβ–ˆβ–…                
                    (103.00 kb …   3.47 mb)   2.44 mb β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ƒβ–‚β–‚β–ƒβ–β–β–β–‚β–‚β–ƒβ–β–„β–β–‚β–β–‚

xml2js                         3.72 ms/iter   3.69 ms β–ˆ                    
                        (3.61 ms … 4.86 ms)   4.36 ms β–ˆ                    
                    (  1.31 mb …   3.22 mb)   1.74 mb β–ˆβ–†β–„β–‚β–‚β–‚β–‚β–‚β–β–‚β–β–β–β–‚β–‚β–β–β–β–β–β–‚

xml-js                         2.50 ms/iter   2.47 ms β–ˆ                    
                        (2.44 ms … 3.29 ms)   3.22 ms β–ˆ                    
                    ( 57.36 kb …   2.63 mb)   1.27 mb β–ˆβ–ˆβ–ƒβ–‚β–β–β–β–β–β–β–β–β–β–β–β–β–β–β–β–β–

summary
  camaro v6
   3.08x faster than txml
   13.71x faster than xml-js
   14.52x faster than fast-xml-parser
   20.35x faster than xml2js

The XML file is an actual XML response from the Expedia API. I just deleted some nodes to change its size for benchmarking.

For complete benchmark, see benchmark.

  • Please note that this is an unfair game for camaro because it only transforms the fields specified in the template. The whole reason for me creating this is because most of the time, I'm just interested in some of the data in the whole XML mess.
  • 🚧 Performance on small XML strings will probably be worse than pure JavaScript implementations. If your use cases consists of small XML strings only, you probably don't need this.

intro

Installation

pnpm add camaro
# npm install camaro

Usage

You can use our custom template format powered by XPath.

We also introduce some custom syntax such as:

  • if a path starts with #, that means it's a constant. E.g, #1234 will return 1234
  • if a path is empty, return blank
  • Some string manipulation functions which are not availble in XPath 1.0, such as lower-case, upper-case, title-case, camel-case, snake-case, string-join or raw. Eventually, I'm hoping to add all XPath 2.0 functions but these are all that I need for now. PRs are welcome.

The rest are pretty much vanilla XPath 1.0.

For complete API documentation, please see API.md

Additional examples can be found in the examples folder or this comprehensive blog post by Ming Di Leom.

const { transform, prettyPrint } = require('camaro')

const xml = `
    <players>
        <player jerseyNumber="10">
            <name>wayne rooney</name>
            <isRetired>false</isRetired>
            <yearOfBirth>1985</yearOfBirth>
        </player>
        <player jerseyNumber="7">
            <name>cristiano ronaldo</name>
            <isRetired>false</isRetired>
            <yearOfBirth>1985</yearOfBirth>
        </player>
        <player jerseyNumber="7">
            <name>eric cantona</name>
            <isRetired>true</isRetired>
            <yearOfBirth>1966</yearOfBirth>
        </player>
    </players>
`

/**
 * the template can be an object or an array depends on what output you want the XML to be transformed to.
 * 
 * ['players/player', {name, ...}] means that: Get all the nodes with this XPath expression `players/player`.
 *      - the first param is the XPath path to get all the XML nodes.
 *      - the second param is a string or an object that describe the shape of the array element and how to get it.
 * 
 * For each of those XML node
 *      - call the XPath function `title-case` on field `name` and assign it to `name` field of the output.
 *      - get the attribute `jerseyNumber` from XML node player
 *      - get the `yearOfBirth` attribute from `yearOfBirth` and cast it to number.
 *      - cast `isRetired` to true if its string value equals to "true", and false otherwise.
 */

const template = ['players/player', {
    name: 'title-case(name)',
    jerseyNumber: '@jerseyNumber',
    yearOfBirth: 'number(yearOfBirth)',
    isRetired: 'boolean(isRetired = "true")'
}]

;(async function () {
    const result = await transform(xml, template)
    console.log(result)

    const prettyStr = await prettyPrint(xml, { indentSize: 4})
    console.log(prettyStr)
})()

Output of transform()

[
    {
        name: 'Wayne Rooney',
        jerseyNumber: 10,
        yearOfBirth: 1985,
        isRetired: false,
    },
    {
        name: 'Cristiano Ronaldo',
        jerseyNumber: 7,
        yearOfBirth: 1985,
        isRetired: false,
    },
    {
        name: 'Eric Cantona',
        jerseyNumber: 7,
        yearOfBirth: 1966,
        isRetired: true,
    }
]

And output of prettyPrint()

<players>
    <player jerseyNumber="10">
        <name>Wayne Rooney</name>
        <isRetired>false</isRetired>
        <yearOfBirth>1985</yearOfBirth>
    </player>
    <player jerseyNumber="7">
        <name>Cristiano Ronaldo</name>
        <isRetired>false</isRetired>
        <yearOfBirth>1985</yearOfBirth>
    </player>
    <player jerseyNumber="7">
        <name>Eric Cantona</name>
        <isRetired>true</isRetired>
        <yearOfBirth>1966</yearOfBirth>
    </player>
</players>

Similar projects

  • cruftless: I personally find this project very fascinating. Its template engine is more powerful than camaro's XPath-based perhaps. You should check it out.

Used by

...

Stargazers over time

Stargazers over time

Licence

The MIT License

About

camaro is a Node.js library that transform XML to JSON, using Node.js binding to native XML parser pugixml, one of the fastest XML parser around.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Sponsor this project

Packages

 
 
 

Contributors