UNPKG

13 kBMarkdownView Raw
1jstoxml
2=========
3[![Build Status](https://travis-ci.org/davidcalhoun/jstoxml.svg?branch=master)](https://travis-ci.org/davidcalhoun/jstoxml)
4[![Downloads][downloads-image]][npm-url]
5
6### Convert JavaScript objects (and JSON) to XML (for RSS, Podcasts, etc.)
7
8Everyone loves JSON, and more and more folks want to move that direction, but we still need things outputted in XML! Particularly for [RSS feeds](http://www.rssboard.org/rss-specification) and [Podcasts](http://www.apple.com/itunes/podcasts/specs.html).
9
10This is inspired by [node-jsontoxml](https://github.com/soldair/node-jsontoxml), which was found to be a bit too rough around the edges. jstoxml attempts to fix that by being more flexible.
11
12### Installation
13* npm install jstoxml
14
15### Changelog
16
17#### Version 1.4.2
18* support for handling arrays of primitives, instead of simply concatenating (#33)
19
20#### Version 1.3.0
21* restored `default` module export (#31)
22
23#### Version 1.2.0
24* refactoring and cleanup
25
26#### Version 1.1.0
27* Added support for attribute filtering (see Example 11b below).
28
29#### Version 1.0.0
30* Complete rewrite! The code should now be easier to understand and maintain.
31* now supports emoji/UTF8 tag attributes (needed for AMP pages - e.g. `<html ⚡ lang="en">`) (see example 14)
32* now supports duplicate attribute key names (see example 15)
33* Fixed: functions returning objects now have now that output passed through toXML for XML conversion
34* Fixed: empty text strings now properly output self-closing tags
35* Migrated tests to mocha
36
37### Examples
38First you'll want to require jstoxml in your script, and assign the result to the namespace variable you want to use (in this case jstoxml):
39
40```javascript
41// Node
42const { toXML } = require('jstoxml');
43
44// Browser (with the help of something like Webpack or Rollup)
45import { toXML } from 'jstoxml';
46
47// Browser global fallback (requires no bundler)
48var toXML = window.jstoxml.toXML;
49```
50
51#### Example 1: Simple object
52```javascript
53toXML({
54 foo: 'bar',
55 foo2: 'bar2'
56});
57```
58Output:
59
60```
61<foo>bar</foo><foo2>bar2</foo2>
62```
63
64Note: because JavaScript doesn't allow duplicate key names, only the last defined key will be outputted. If you need duplicate keys, please use an array instead (see Example 2 below).
65
66
67#### Example 2: Simple array (needed for duplicate keys)
68```javascript
69toXML([
70 {
71 foo: 'bar'
72 },
73 {
74 foo: 'bar2'
75 }
76]);
77```
78Output:
79
80```
81<foo>bar</foo><foo>bar2</foo>
82```
83
84#### Example 3: Simple functions
85```javascript
86toXML({ currentTime: () => new Date() });
87```
88Output:
89
90```
91<currentTime>Mon Oct 02 2017 09:34:54 GMT-0700 (PDT)</currentTime>
92```
93
94
95#### Example 4: XML tag attributes
96```javascript
97toXML({
98 _name: 'foo',
99 _content: 'bar',
100 _attrs: {
101 a: 'b',
102 c: 'd'
103 }
104});
105```
106Output:
107
108```
109<foo a="b" c="d">bar</foo>
110```
111
112
113#### Example 5: Tags mixed with text content
114To output text content, set a key to null:
115
116```javascript
117toXML({
118 'text1': null,
119 foo: 'bar',
120 'text2': null
121});
122
123```
124Output:
125
126```
127text1<foo>bar</foo>text2
128```
129
130
131#### Example 6: Nested tags (with indenting)
132
133```javascript
134const xmlOptions = {
135 header: false,
136 indent: ' '
137};
138
139toXML({
140 a: {
141 foo: 'bar',
142 foo2: 'bar2'
143 }
144}, xmlOptions);
145```
146Output:
147
148```
149<a>
150 <foo>bar</foo>
151 <foo2>bar2</foo2>
152</a>
153```
154
155
156#### Example 7: Nested tags with attributes (with indenting)
157```javascript
158const xmlOptions = {
159 header: false,
160 indent: ' '
161};
162
163toXML({
164 ooo: {
165 _name: 'foo',
166 _attrs: {
167 a: 'b'
168 },
169 _content: {
170 _name: 'bar',
171 _attrs: {
172 c: 'd'
173 }
174 }
175 }
176}, xmlOptions);
177```
178Output:
179
180```
181<ooo>
182 <foo a="b">
183 <bar c="d"/>
184 </foo>
185</ooo>
186```
187
188Note that cases like this might be especially hard to read because of the deep nesting, so it's recommend you use something like this pattern instead, which breaks it up into more readable pieces:
189
190```javascript
191const bar = {
192 _name: 'bar',
193 _attrs: {
194 c: 'd'
195 }
196};
197
198const foo = {
199 _name: 'foo',
200 _attrs: {
201 a: 'b'
202 },
203 _content: bar
204};
205
206const xmlOptions = {
207 header: false,
208 indent: ' '
209};
210
211return toXML({
212 ooo: foo
213},
214xmlOptions);
215```
216
217
218#### Example 8: Complex functions
219Function outputs will be processed (fed back into toXML), meaning that you can output objects that will in turn be converted to XML.
220
221```javascript
222toXML({
223 someNestedXML: () => {
224 return {
225 foo: 'bar'
226 }
227 }
228});
229```
230Output:
231
232```
233<someNestedXML><foo>bar</foo></someNestedXML>
234```
235
236#### Example 9: RSS Feed
237```javascript
238const xmlOptions = {
239 header: true,
240 indent: ' '
241};
242
243toXML({
244 _name: 'rss',
245 _attrs: {
246 version: '2.0'
247 },
248 _content: {
249 channel: [
250 {
251 title: 'RSS Example'
252 },
253 {
254 description: 'Description'},
255 {
256 link: 'google.com'},
257 {
258 lastBuildDate: () => new Date()
259 },
260 {
261 pubDate: () => new Date()
262 },
263 {
264 language: 'en'},
265 {
266 item: {
267 title: 'Item title',
268 link: 'Item link',
269 description: 'Item Description',
270 pubDate: () => new Date()
271 }
272 },
273 {
274 item: {
275 title: 'Item2 title',
276 link: 'Item2 link',
277 description: 'Item2 Description',
278 pubDate: () => new Date()
279 }
280 }
281 ]
282 }
283}, xmlOptions);
284```
285Output:
286
287```
288<?xml version="1.0" encoding="UTF-8"?>
289<rss version="2.0">
290 <channel>
291 <title>RSS Example</title>
292 <description>Description</description>
293 <link>google.com</link>
294 <lastBuildDate>Sat Jul 30 2011 18:14:25 GMT+0900 (JST)</lastBuildDate>
295 <pubDate>Sat Jul 30 2011 18:14:25 GMT+0900 (JST)</pubDate>
296 <language>en</language>
297 <item>
298 <title>Item title</title>
299 <link>Item link</link>
300 <description>Item Description</description>
301 <pubDate>Sat Jul 30 2011 18:33:47 GMT+0900 (JST)</pubDate>
302 </item>
303 <item>
304 <title>Item2 title</title>
305 <link>Item2 link</link>
306 <description>Item2 Description</description>
307 <pubDate>Sat Jul 30 2011 18:33:47 GMT+0900 (JST)</pubDate>
308 </item>
309 </channel>
310</rss>
311```
312
313
314#### Example 10: Podcast RSS Feed
315(see the [Apple docs](http://www.apple.com/itunes/podcasts/specs.html) for more information)
316
317```javascript
318const xmlOptions = {
319 header: true,
320 indent: ' '
321};
322
323toXML({
324 _name: 'rss',
325 _attrs: {
326 'xmlns:itunes': 'http://www.itunes.com/dtds/podcast-1.0.dtd',
327 version: '2.0'
328 },
329 _content: {
330 channel: [
331 {
332 title: 'Title'
333 },
334 {
335 link: 'google.com'
336 },
337 {
338 language: 'en-us'
339 },
340 {
341 copyright: 'Copyright 2011'
342 },
343 {
344 'itunes:subtitle': 'Subtitle'
345 },
346 {
347 'itunes:author': 'Author'
348 },
349 {
350 'itunes:summary': 'Summary'
351 },
352 {
353 description: 'Description'
354 },
355 {
356 'itunes:owner': {
357 'itunes:name': 'Name',
358 'itunes:email': 'Email'
359 }
360 },
361 {
362 _name: 'itunes:image',
363 _attrs: {
364 href: 'image.jpg'
365 }
366 },
367 {
368 _name: 'itunes:category',
369 _attrs: {
370 text: 'Technology'
371 },
372 _content: {
373 _name: 'itunes:category',
374 _attrs: {
375 text: 'Gadgets'
376 }
377 }
378 },
379 {
380 _name: 'itunes:category',
381 _attrs: {
382 text: 'TV &amp; Film'
383 }
384 },
385 {
386 item: [
387 {
388 title: 'Podcast Title'
389 },
390 {
391 'itunes:author': 'Author'
392 },
393 {
394 'itunes:subtitle': 'Subtitle'
395 },
396 {
397 'itunes:summary': 'Summary'
398 },
399 {
400 'itunes:image': 'image.jpg'
401 },
402 {
403 _name: 'enclosure',
404 _attrs: {
405 url: 'http://example.com/podcast.m4a',
406 length: '8727310',
407 type: 'audio/x-m4a'
408 }
409 },
410 {
411 guid: 'http://example.com/archive/aae20050615.m4a'
412 },
413 {
414 pubDate: 'Wed, 15 Jun 2011 19:00:00 GMT'
415 },
416 {
417 'itunes:duration': '7:04'
418 },
419 {
420 'itunes:keywords': 'salt, pepper, shaker, exciting'
421 }
422 ]
423 },
424 {
425 item: [
426 {
427 title: 'Podcast2 Title'
428 },
429 {
430 'itunes:author': 'Author2'
431 },
432 {
433 'itunes:subtitle': 'Subtitle2'
434 },
435 {
436 'itunes:summary': 'Summary2'
437 },
438 {
439 'itunes:image': 'image2.jpg'
440 },
441 {
442 _name: 'enclosure',
443 _attrs: {
444 url: 'http://example.com/podcast2.m4a',
445 length: '655555',
446 type: 'audio/x-m4a'
447 }
448 },
449 {
450 guid: 'http://example.com/archive/aae2.m4a'
451 },
452 {
453 pubDate: 'Wed, 15 Jul 2011 19:00:00 GMT'
454 },
455 {
456 'itunes:duration': '11:20'
457 },
458 {
459 'itunes:keywords': 'foo, bar'
460 }
461 ]
462 }
463 ]
464 }
465}, xmlOptions);
466```
467
468Output:
469
470```
471<?xml version="1.0" encoding="UTF-8"?>
472<rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" version="2.0">
473 <channel>
474 <title>Title</title>
475 <link>google.com</link>
476 <language>en-us</language>
477 <copyright>Copyright 2011</copyright>
478 <itunes:subtitle>Subtitle</itunes:subtitle>
479 <itunes:author>Author</itunes:author>
480 <itunes:summary>Summary</itunes:summary>
481 <description>Description</description>
482 <itunes:owner>
483 <itunes:name>Name</itunes:name>
484 <itunes:email>Email</itunes:email>
485 </itunes:owner>
486 <itunes:image href="image.jpg"/>
487 <itunes:category text="Technology">
488 <itunes:category text="Gadgets"/>
489 </itunes:category>
490 <itunes:category text="TV &amp; Film"/>
491 <item>
492 <title>Podcast Title</title>
493 <itunes:author>Author</itunes:author>
494 <itunes:subtitle>Subtitle</itunes:subtitle>
495 <itunes:summary>Summary</itunes:summary>
496 <itunes:image>image.jpg</itunes:image>
497 <enclosure url="http://example.com/podcast.m4a" length="8727310" type="audio/x-m4a"/>
498 <guid>http://example.com/archive/aae20050615.m4a</guid>
499 <pubDate>Wed, 15 Jun 2011 19:00:00 GMT</pubDate>
500 <itunes:duration>7:04</itunes:duration>
501 <itunes:keywords>salt, pepper, shaker, exciting</itunes:keywords>
502 </item>
503 <item>
504 <title>Podcast2 Title</title>
505 <itunes:author>Author2</itunes:author>
506 <itunes:subtitle>Subtitle2</itunes:subtitle>
507 <itunes:summary>Summary2</itunes:summary>
508 <itunes:image>image2.jpg</itunes:image>
509 <enclosure url="http://example.com/podcast2.m4a" length="655555" type="audio/x-m4a"/>
510 <guid>http://example.com/archive/aae2.m4a</guid>
511 <pubDate>Wed, 15 Jul 2011 19:00:00 GMT</pubDate>
512 <itunes:duration>11:20</itunes:duration>
513 <itunes:keywords>foo, bar</itunes:keywords>
514 </item>
515 </channel>
516</rss>
517```
518
519#### Example 11: Custom filter for XML entities, or whatever
520
521```javascript
522const xmlOptions = {
523 filter: {
524 '<': '&lt;',
525 '>': '&gt;',
526 '"': '&quot;',
527 '\'': '&apos;',
528 '&': '&amp;'
529 }
530};
531
532toXML({
533 foo: '<a>',
534 bar: '"b"',
535 baz: '\'&whee\''
536}, xmlOptions);
537```
538
539Output:
540
541```
542<foo>&lt;a&gt;</foo><bar>&quot;b&quot;</bar><baz>&apos;&amp;whee&apos;</baz>
543```
544
545#### Example 11b: Custom filter for XML attributes
546
547```javascript
548const xmlOptions = {
549 attributesFilter: {
550 '<': '&lt;',
551 '>': '&gt;',
552 '"': '&quot;',
553 '\'': '&apos;',
554 '&': '&amp;'
555 }
556};
557
558toXML({
559 _name: 'foo',
560 _attrs: { a: '<"\'&"foo>' }
561}, xmlOptions);
562```
563
564Output:
565
566```
567<foo a="&lt;&quot;&apos;&amp;&quot;foo&gt;"/>
568```
569
570
571#### Example 12: Avoiding self-closing tags
572If for some reason you want to avoid self-closing tags, you can pass in a special config option `_selfCloseTag`:
573
574```javascript
575const xmlOptions = {
576 _selfCloseTag: false
577};
578
579toXML({
580 foo: '',
581 bar: undefined
582}, xmlOptions);
583```
584
585Output:
586
587```
588<foo></foo><bar>whee</bar>
589```
590
591#### Example 13: Custom XML header
592
593```javascript
594const xmlOptions = {
595 header: '<?xml version="1.0" encoding="UTF-16" standalone="yes"?>'
596};
597
598toXML({
599 foo: 'bar'
600}, xmlOptions);
601```
602
603Output:
604
605```
606<?xml version="1.0" encoding="UTF-16" standalone="yes"?><foo>bar</foo><foo2>bar2</foo2>
607```
608
609#### Example 14: Emoji attribute support (needed for AMP)
610
611```javascript
612toXML({
613 html: {
614 _attrs: {
615 '⚡': true
616 }
617 }
618});
619```
620
621Output:
622
623```
624<html ⚡/>
625```
626
627#### Example 15: Duplicate attribute key support
628
629```javascript
630toXML({
631 html: {
632 _attrs: [
633 {
634 lang: 'en'
635 },
636 {
637 lang: 'klingon'
638 }
639 ]
640 }
641});
642```
643
644Output:
645
646```
647<html lang="en" lang="klingon"/>
648```
649
650### License
651MIT
652
653[downloads-image]: https://img.shields.io/npm/dm/jstoxml.svg?style=flat-square
654[npm-url]: https://www.npmjs.com/package/jstoxml
655[npm-image]: https://img.shields.io/npm/dm/jstoxml.svg?style=flat
656