{"id":13398697,"url":"https://github.com/tuananh/camaro","last_synced_at":"2025-05-15T17:09:18.913Z","repository":{"id":42163785,"uuid":"93039449","full_name":"tuananh/camaro","owner":"tuananh","description":"camaro is an utility to transform XML to JSON, using Node.js binding to native XML parser pugixml, one of the fastest XML parser around.","archived":false,"fork":false,"pushed_at":"2024-03-16T00:17:23.000Z","size":2848,"stargazers_count":552,"open_issues_count":9,"forks_count":28,"subscribers_count":9,"default_branch":"develop","last_synced_at":"2024-05-22T03:03:42.072Z","etag":null,"topics":["camaro","emscripten","json","pugixml","transform","wasm","webassembly","xml","xml-parser","xml2json","xpath"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tuananh.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["tuananh"],"patreon":"tuananh_org"}},"created_at":"2017-06-01T09:19:01.000Z","updated_at":"2024-06-04T08:27:00.919Z","dependencies_parsed_at":"2024-03-14T02:25:13.172Z","dependency_job_id":"98d0da86-1e2f-45a1-88d8-b4a6541df0b1","html_url":"https://github.com/tuananh/camaro","commit_stats":{"total_commits":456,"total_committers":13,"mean_commits":35.07692307692308,"dds":0.08991228070175439,"last_synced_commit":"6118e92859d8007512e0aa96b98ecf23f963cf0d"},"previous_names":[],"tags_count":81,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tuananh%2Fcamaro","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tuananh%2Fcamaro/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tuananh%2Fcamaro/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tuananh%2Fcamaro/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tuananh","download_url":"https://codeload.github.com/tuananh/camaro/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247737788,"owners_count":20987721,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["camaro","emscripten","json","pugixml","transform","wasm","webassembly","xml","xml-parser","xml2json","xpath"],"created_at":"2024-07-30T19:00:30.767Z","updated_at":"2025-04-07T22:09:31.317Z","avatar_url":"https://github.com/tuananh.png","language":"JavaScript","funding_links":["https://github.com/sponsors/tuananh","https://patreon.com/tuananh_org"],"categories":["Podcast","JavaScript"],"sub_categories":[],"readme":"# camaro\n\n\u003e camaro is a utility to transform XML to JSON, using Node.js bindings to a native XML parser [pugixml](http://pugixml.org/) - one of the fastest XML parsers around.\n\n[![npm](https://raster.shields.io/npm/v/camaro)](https://npm.im/camaro)\n![Build status](https://github.com/tuananh/camaro/workflows/CI/badge.svg)\n[![npm](https://raster.shields.io/npm/dt/camaro)](https://npm.im/camaro)\n[![license](https://raster.shields.io/npm/l/camaro)](https://npm.im/camaro)\n\n## 🤘 Features\n\n* Transform XML to JSON.\n    * Only take properties that you're interested in.\n    * Output is ready to use JS object.\n    * For those that need a complete document parser, checkout my other project [@tuananh/sax-parser](https://github.com/tuananh/sax-parser) - a pretty fast native module, XML-compliant SAX parser for Node.js.\n\n* Written in C++ and compiled down to WebAssembly, so no re-compilation needed.\n    * No need to build a binary whenever a new Node version is released.\n    * Works on all major platforms (OS X, Linux and Windows). See Travis CI and AppVeyor build status for details.\n    * AWS Lambda friendly (or serverless in general).\n\n* It's pretty fast on large XML strings.\n    * We're using [pugixml](http://pugixml.org/) under the hood. It's one of the fastest XML parsers around.\n    * Scales well with multi-core processors by use of a `worker_threads` pool (Node \u003e= 12).\n\n* Pretty print XML.\n\n## 🔥 Benchmark\n\n300 KB XML file                      |  100 KB XML file\n:-----------------------------------:|:-------------------------:\n![](benchmark/fixtures/300kb.png)    |  ![](benchmark/fixtures/100kb.png)\n\n60 KB XML file                       |  7 KB XML file\n:-----------------------------------:|:-------------------------:\n![](benchmark/fixtures/60kb.png)     |  ![](benchmark/fixtures/7kb.png)\n\nThe XML file is an actual XML response from the Expedia API. I just deleted some nodes to change its size for benchmarking.\n\nFor complete benchmark, see [benchmark/README.md](benchmark/README.md).\n\n* Please note that **this is an unfair game for camaro** because it only transforms the fields specified in the template.\nThe 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.\n* I may expose another method to transform the whole XML tree so that the benchmark will better reflect the real performance.\n* 🚧 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.\n* Some other libraries that I used to use for benchmarks, like `rapidx2j` and `xml2json`, no longer work on Node 14, so I removed them from the benchmark.\n\n![intro](intro.png)\n\n## Installation\n\n```sh\nyarn add camaro\n# npm install camaro\n```\n\n## Usage\n\nYou can use our custom template format powered by [XPath](https://developer.mozilla.org/en-US/docs/Web/XPath).\n\nWe also introduce some custom syntax such as:\n\n* if a path starts with `#`, that means it's a constant. E.g, `#1234` will return `1234`\n* if a path is empty, return blank\n* 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.\n\nThe rest are pretty much vanilla XPath 1.0.\n\nFor complete API documentation, please see [API.md](API.md)\n\nAdditional examples can be found in the examples folder at https://github.com/tuananh/camaro/tree/develop/examples or this comprehensive [blog post](https://mdleom.com/blog/2020/01/20/how-to-use-camaro-xml/) by Ming Di Leom.\n\n```js\nconst { transform, prettyPrint } = require('camaro')\n\nconst xml = `\n    \u003cplayers\u003e\n        \u003cplayer jerseyNumber=\"10\"\u003e\n            \u003cname\u003ewayne rooney\u003c/name\u003e\n            \u003cisRetired\u003efalse\u003c/isRetired\u003e\n            \u003cyearOfBirth\u003e1985\u003c/yearOfBirth\u003e\n        \u003c/player\u003e\n        \u003cplayer jerseyNumber=\"7\"\u003e\n            \u003cname\u003ecristiano ronaldo\u003c/name\u003e\n            \u003cisRetired\u003efalse\u003c/isRetired\u003e\n            \u003cyearOfBirth\u003e1985\u003c/yearOfBirth\u003e\n        \u003c/player\u003e\n        \u003cplayer jerseyNumber=\"7\"\u003e\n            \u003cname\u003eeric cantona\u003c/name\u003e\n            \u003cisRetired\u003etrue\u003c/isRetired\u003e\n            \u003cyearOfBirth\u003e1966\u003c/yearOfBirth\u003e\n        \u003c/player\u003e\n    \u003c/players\u003e\n`\n\n/**\n * the template can be an object or an array depends on what output you want the XML to be transformed to.\n * \n * ['players/player', {name, ...}] means that: Get all the nodes with this XPath expression `players/player`.\n *      - the first param is the XPath path to get all the XML nodes.\n *      - the second param is a string or an object that describe the shape of the array element and how to get it.\n * \n * For each of those XML node\n *      - call the XPath function `title-case` on field `name` and assign it to `name` field of the output.\n *      - get the attribute `jerseyNumber` from XML node player\n *      - get the `yearOfBirth` attribute from `yearOfBirth` and cast it to number.\n *      - cast `isRetired` to true if its string value equals to \"true\", and false otherwise.\n */\n\nconst template = ['players/player', {\n    name: 'title-case(name)',\n    jerseyNumber: '@jerseyNumber',\n    yearOfBirth: 'number(yearOfBirth)',\n    isRetired: 'boolean(isRetired = \"true\")'\n}]\n\n;(async function () {\n    const result = await transform(xml, template)\n    console.log(result)\n\n    const prettyStr = await prettyPrint(xml, { indentSize: 4})\n    console.log(prettyStr)\n})()\n```\n\nOutput of `transform()`\n\n```\n[\n    {\n        name: 'Wayne Rooney',\n        jerseyNumber: 10,\n        yearOfBirth: 1985,\n        isRetired: false,\n    },\n    {\n        name: 'Cristiano Ronaldo',\n        jerseyNumber: 7,\n        yearOfBirth: 1985,\n        isRetired: false,\n    },\n    {\n        name: 'Eric Cantona',\n        jerseyNumber: 7,\n        yearOfBirth: 1966,\n        isRetired: true,\n    }\n]\n```\n\nAnd output of `prettyPrint()`\n\n```\n\u003cplayers\u003e\n    \u003cplayer jerseyNumber=\"10\"\u003e\n        \u003cname\u003eWayne Rooney\u003c/name\u003e\n        \u003cisRetired\u003efalse\u003c/isRetired\u003e\n        \u003cyearOfBirth\u003e1985\u003c/yearOfBirth\u003e\n    \u003c/player\u003e\n    \u003cplayer jerseyNumber=\"7\"\u003e\n        \u003cname\u003eCristiano Ronaldo\u003c/name\u003e\n        \u003cisRetired\u003efalse\u003c/isRetired\u003e\n        \u003cyearOfBirth\u003e1985\u003c/yearOfBirth\u003e\n    \u003c/player\u003e\n    \u003cplayer jerseyNumber=\"7\"\u003e\n        \u003cname\u003eEric Cantona\u003c/name\u003e\n        \u003cisRetired\u003etrue\u003c/isRetired\u003e\n        \u003cyearOfBirth\u003e1966\u003c/yearOfBirth\u003e\n    \u003c/player\u003e\n\u003c/players\u003e\n```\n\n## Similar projects\n\n- [cruftless](https://github.com/wspringer/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.\n\n## Used by\n\n- https://github.com/dsifford/academic-bloggers-toolkit\n- https://github.com/hexojs/hexo-generator-sitemap\n- https://github.com/hexojs/hexo-generator-feed\n- https://github.com/hexojs/hexo-migrator-wordpress\n- https://github.com/fengkx/NodeRSSBot\n\n...\n\n## Stargazers over time\n\n[![Stargazers over time](https://starchart.cc/tuananh/camaro.svg)](https://starchart.cc/tuananh/camaro)\n\n## Licence\n\n[The MIT License](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftuananh%2Fcamaro","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftuananh%2Fcamaro","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftuananh%2Fcamaro/lists"}