├── .gitignore ├── .travis.yml ├── AUTHORS ├── LICENSE ├── README.md ├── examples ├── demonstrator ├── sample01.json ├── sample01.xml ├── sample02.json ├── sample02.xml ├── xml-mirror └── xml2json ├── index.js ├── lib └── xml-mapping.js ├── package.json └── test ├── basic.js ├── nested.js ├── parserinfos.js ├── tojson.js └── toxml.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.tgz 2 | *.swp 3 | *.swo 4 | /node_modules 5 | README.html 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.6 4 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # Authors 2 | Nicolas Thouvenin (http://blog.touv.fr/) 3 | Joe Ibershoff (https://github.com/zacronos) 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2013 Nicolas Thouvenin 2 | 3 | This project is free software released under the MIT/X11 license: 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # xml2json and json2xml for NodeJS 2 | 3 | [![Build Status](https://secure.travis-ci.org/Inist-CNRS/node-xml-mapping.png?branch=master)](http://travis-ci.org/Inist-CNRS/node-xml-mapping) 4 | 5 | It's native javascript implementation of a bidirectional converter between XML and JS data structure (aka JSON). 6 | You can convert any type of XML documents in an Javascript data structure. 7 | You can also do the reverse, converting a Javascript data structure in XML String. XML is still valid. 8 | 9 | ## Contributors 10 | 11 | * [Nicolas Thouvenin](https://github.com/touv) 12 | * [Joe Ibershoff](https://github.com/zacronos) 13 | * [Yura Zenevich](https://github.com/yzen) 14 | * [Thorsten Lorenz](https://github.com/thlorenz) 15 | * [ntgn81](https://github.com/ntgn81) 16 | * [Rowell Cruz](https://github.com/rcruz) 17 | * [JT](https://github.com/spikyjt) 18 | * [Bindu Wavell](https://github.com/binduwavell) 19 | * [niederlec](https://github.com/niederlec) 20 | 21 | # Installation 22 | 23 | With [npm](http://npmjs.org) do: 24 | 25 | $ npm install xml-mapping 26 | 27 | 28 | # Usage 29 | ```javascript 30 | var XMLMapping = require('xml-mapping'); 31 | 32 | var json = XMLMapping.load('value'); 33 | var xml = XMLMapping.dump(json); 34 | 35 | console.log(xml,json); 36 | console.log(json); 37 | ``` 38 | 39 | Output: 40 | 41 | value { key: { '$t': 'value' } } 42 | 43 | # Convention 44 | 45 | The rules for converting XML to JSON are those used by Google in its GData protocol. More information here : http://code.google.com/apis/gdata/docs/json.html 46 | 47 | # Tests 48 | 49 | Use [nodeunit](https://github.com/caolan/nodeunit) to run the tests. 50 | 51 | $ npm install nodeunit 52 | $ nodeunit test 53 | 54 | # API Documentation 55 | 56 | ## load(String xml, Object options) 57 | Transform a string with XML in Javascript data structure (JSON). 58 | **Return Object.** 59 | 60 | ###Options 61 | 62 | **Warning : options break the symmetry. This means that the JSON generated may not reproduce the same XML** 63 | 64 | * `throwErrors` - *boolean* - Flag to throw errors of the SAX Parser ; *default : false* 65 | * `nested` - *boolean* - Flag to ignore nested tags inside text : *default : false* 66 | * `specialChar` - *string* - Set the first character of XML tag ($t, $text, $cd, $cdata, $e, $element, $c, $comment); *default : $* 67 | * `longTag` - *boolean* - Use long names tags($text, $element, $cdata, $comment) rather than short names ($t, $cd, $e, $c); *default : false* 68 | * `comments` - *boolean* - Flag to ignore comments, if false all the comments will be ignored : *default : true* 69 | * `parserInfos` - *boolean* - Flag to add some attributes generated by the parser (order, line, column, name), if true informations are inserted in the attributes list of all tags : *default : false* 70 | 71 | ```javascript 72 | var xml = 'Title is <strong>important</strong>'; 73 | var json = XMLMapping.load(xml, { nested: true }); 74 | console.log(json); 75 | 76 | // Should output: 77 | // { title : { $t : 'Title isimportant', strong: { '$t': 'important' }} } 78 | ``` 79 | 80 | * `arrays` - *array* - an array of basic XPath strings that specify XML nodes that should be array, even when there is only one such node. 81 | 82 | ```javascript 83 | var xml = 'value1value3'; 84 | var json = XMLMapping.load(xml, { 85 | arrays: [ 86 | '/key1', 87 | '/key2/key3' 88 | ] 89 | }); 90 | console.log(json); 91 | 92 | // Should output: 93 | // (Note that value of key1 and key3 are arrays, as specified in options) 94 | // {"key1":[{"$t":"value1"}],"key2":{"key3":[{"$t":"value3"}]}} 95 | ``` 96 | 97 | 98 | ## dump(Object json, Object options) 99 | Transform a Javascript data structure (JSON) in XML string. **Return String.** 100 | 101 | ###Options 102 | 103 | **Warning : options break the symmetry. This means that the XML generated may not reproduce the same JSON** 104 | 105 | * `indent` - *boolean* - Flag to throw errors of the SAX Parser ; *default : false* 106 | * `header` - *boolean* - Flag to add XML header; *default : false* 107 | * `version` - *string* - Set version attribute of XML header (see header flag); *default : 1.0* 108 | * `encoding` - *string* - Set encoding attribute of XML header (see header flag); *default : UTF-8* 109 | * `specialChar` - *string* - Set the first character of XML tag ($t, $text, $cd, $cdata, $e, $element, $c, $comment); *default : $* 110 | 111 | 112 | ## tojson(String xml) 113 | Alias of load. 114 | 115 | ## toxml(Object json) 116 | Alias of dump. 117 | 118 | # Also 119 | 120 | * https://github.com/estheban/node-json2xml 121 | * https://github.com/buglabs/node-xml2json 122 | 123 | # License 124 | 125 | [MIT/X11](https://raw.github.com/touv/node-xml-mapping/master/LICENSE) 126 | 127 | 128 | [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/touv/node-xml-mapping/trend.png)](https://bitdeli.com/free "Bitdeli Badge") 129 | 130 | -------------------------------------------------------------------------------- /examples/demonstrator: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var xm = require('../lib/xml-mapping.js') 4 | ,util = require('util') 5 | ,assert = require('assert'); 6 | 7 | [ 8 | 'value' 9 | ,'value2' 10 | ,'value1value2' 11 | ,'value1value2' 12 | ,'value2' 13 | ,'' 14 | ,'' 15 | ].forEach(function(xml) { 16 | var json = xm.tojson(xml); 17 | process.stdout.write(xml); 18 | process.stdout.write('\n'); 19 | process.stdout.write(util.inspect(json, false, null, true)); 20 | process.stdout.write('\n\n'); 21 | assert.equal(xml, xm.toxml(json)); 22 | }) 23 | 24 | 25 | //,'valvalue2ue1' 26 | 27 | -------------------------------------------------------------------------------- /examples/sample01.json: -------------------------------------------------------------------------------- 1 | { feed: 2 | { xmlns: 'http://www.w3.org/2005/Atom', 3 | 'xmlns:openSearch': 'http://a9.com/-/spec/opensearchrss/1.0/', 4 | 'xmlns:gd': 'http://schemas.google.com/g/2005', 5 | 'xmlns:gCal': 'http://schemas.google.com/gCal/2005', 6 | id: { '$t': 'XXX' }, 7 | updated: { '$t': '2006-11-12T21:25:30.000Z' }, 8 | title: { type: 'text', '$t': 'Google Developer Events' }, 9 | subtitle: 10 | { type: 'text', 11 | '$t': 'The calendar contains information about upcoming developer conferences at which Google will be speaking, along with other developer-related events.' }, 12 | link: 13 | [ { rel: 'http://schemas.google.com/g/2005#feed', 14 | type: 'application/atom+xml', 15 | href: 'XXX' }, 16 | { rel: 'self', type: 'application/atom+xml', href: 'XXX' } ], 17 | author: 18 | { name: { '$t': 'Google Developer Calendar' }, 19 | email: { '$t': 'developer-calendar@google.com' } }, 20 | generator: 21 | { version: '1.0', 22 | uri: 'http://www.google.com/calendar', 23 | '$t': 'Google Calendar' }, 24 | 'openSearch:startIndex': { '$t': '1' }, 25 | 'openSearch:itemsPerPage': { '$t': '25' }, 26 | 'gCal:timezone': { value: 'America/Los_Angeles' }, 27 | entry: 28 | { id: { '$t': 'XXX' }, 29 | published: { '$t': '2006-11-12T21:25:30.000Z' }, 30 | updated: { '$t': '2006-11-12T21:25:30.000Z' }, 31 | category: 32 | [ { scheme: 'XXX', term: 'XXX' }, 33 | { scheme: 'YYY', term: 'YYY' } ], 34 | title: 35 | { type: 'text', 36 | '$t': 'WebmasterWorld PubCon 2006: Google Developer Tools in General' }, 37 | content: 38 | { type: 'text', 39 | '$t': 'Google is sponsoring at WebmasterWorld PubCon 2006. Come and visit us at the booth or join us for an evening demo reception where we will be talking "5 ways to enhance your website with Google Code". After all, it is Vegas, baby! See you soon.' }, 40 | link: 41 | [ { rel: 'alternate', 42 | type: 'text/html', 43 | href: 'XXX', 44 | title: 'alternate' }, 45 | { rel: 'self', type: 'application/atom+xml', href: 'XXX' } ], 46 | author: 47 | { name: { '$t': 'Google Developer Calendar' }, 48 | email: { '$t': 'developer-calendar@google.com' } }, 49 | 'gCal:sendEventNotifications': { value: 'true' }, 50 | 'gd:comments': { 'gd:feedLink': { href: 'XXX' } }, 51 | 'gd:transparency': { value: 'XXX' }, 52 | 'gd:eventStatus': { value: 'XXX' }, 53 | 'gd:where': { valueString: '3150 Paradise Road, Las Vegas, NV 89109' }, 54 | 'gd:when': 55 | { startTime: '2006-11-15', 56 | endTime: '2006-11-17', 57 | 'gd:reminder': { minutes: '10' } } }, 58 | '$c': 'Etc.' } } -------------------------------------------------------------------------------- /examples/sample01.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | XXX 4 | 2006-11-12T21:25:30.000Z 5 | Google Developer Events 6 | The calendar contains information about upcoming developer conferences at which Google will be speaking, along with other developer-related events. 7 | 8 | 9 | 10 | Google Developer Calendar 11 | developer-calendar@google.com 12 | 13 | Google Calendar 14 | 1 15 | 25 16 | 17 | 18 | XXX 19 | 2006-11-12T21:25:30.000Z 20 | 2006-11-12T21:25:30.000Z 21 | 22 | 23 | WebmasterWorld PubCon 2006: Google Developer Tools in General 24 | Google is sponsoring at <a href="http://www.pubcon.com/">WebmasterWorld PubCon 2006</a>. Come and visit us at the booth or join us for an evening demo reception where we will be talking "5 ways to enhance your website with Google Code". After all, it is Vegas, baby! See you soon. 25 | 26 | 27 | 28 | Google Developer Calendar 29 | developer-calendar@google.com 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /examples/sample02.json: -------------------------------------------------------------------------------- 1 | { rss: 2 | { 'xmlns:atom': 'http://www.w3.org/2005/Atom', 3 | 'xmlns:dc': 'http://purl.org/dc/elements/1.1/', 4 | 'xmlns:opensearch': 'http://a9.com/-/spec/opensearch/1.1/', 5 | version: '2.0', 6 | channel: 7 | { title: { '$t': 'Web - WordPress.com Search' }, 8 | link: { '$t': 'http://fr.search.wordpress.com/?q=Web' }, 9 | description: { '$t': 'Web - WordPress.com Search' }, 10 | pubDate: { '$t': 'Wed, 22 Feb 2012 13:32:27 +0000' }, 11 | language: { '$t': 'fr' }, 12 | image: 13 | { url: { '$t': 'http://s.wordpress.com/i/buttonw-com.png' }, 14 | width: { '$t': '224' }, 15 | height: { '$t': '58' }, 16 | title: { '$t': 'WordPress.com' }, 17 | link: { '$t': 'http://wordpress.com/' } }, 18 | generator: { '$t': 'http://search.wordpress.com/' }, 19 | 'atom:link': 20 | [ { rel: 'self', 21 | type: 'application/rss+xml', 22 | href: 'http://fr.search.wordpress.com/?q=Web&f=feed' }, 23 | { rel: 'search', 24 | type: 'application/opensearchdescription+xml', 25 | href: 'http://en.search.wordpress.com/opensearch.xml', 26 | title: 'WordPress.com' } ], 27 | 'opensearch:totalResults': { '$t': '175575' }, 28 | 'opensearch:startIndex': { '$t': '1' }, 29 | 'opensearch:itemsPerPage': { '$t': '10' }, 30 | 'opensearch:Query': { role: 'request', searchTerms: 'Web' }, 31 | item: 32 | [ { title: { '$cd': 'Web' }, 33 | link: { '$t': 'http://zapistage.wordpress.com/2010/12/01/web/' }, 34 | pubDate: { '$t': 'Wed, 01 Dec 2010 10:08:53 +0000' }, 35 | 'dc:creator': { '$t': 'zapithomas' }, 36 | guid: 37 | { isPermaLink: 'true', 38 | '$t': 'http://zapistage.wordpress.com/2010/12/01/web/' }, 39 | description: { '$cd': 'Voilà la définition du web:\n\t\t\t\t- Réseau informatique mondial constitué d'un ensemble de réseaux nationaux, régionaux et privés qui sont reliés par le protocole de communication TCP/IP et qui […]' }, 40 | source: 41 | { url: 'http://zapistage.wordpress.com/feed/', 42 | '$t': 'Un stage chez Zapilou' } }, 43 | { title: { '$cd': 'Conférence Paris-Web 2009' }, 44 | link: { '$t': 'http://relationspresse.wordpress.com/2009/07/21/conference-paris-web-2009/' }, 45 | pubDate: { '$t': 'Tue, 21 Jul 2009 09:58:13 +0000' }, 46 | 'dc:creator': { '$t': 'Pascal Gibert' }, 47 | guid: 48 | { isPermaLink: 'true', 49 | '$t': 'http://relationspresse.wordpress.com/2009/07/21/conference-paris-web-2009/' }, 50 | description: { '$cd': '\n\n\t\t\t\tCommuniqué de presse Conférence Paris-Web 2009\n\t\t\t\tLa quatrième conférence Paris-Web se déroulera les 8 et 9 octobre 2009 à Paris-La Défense. Les organisateurs de la quatrième conférence Paris-Web publient […]' }, 51 | source: 52 | { url: 'http://relationspresse.wordpress.com/feed/', 53 | '$t': 'News Presse, Actualités et Informations; Digital Citizen, Press News and Information' } }, 54 | { title: { '$cd': 'développement de sites web offshore' }, 55 | link: { '$t': 'http://agenceweboffshore.wordpress.com/2009/04/30/developpement-de-sites-web-offshore/' }, 56 | pubDate: { '$t': 'Thu, 30 Apr 2009 19:10:38 +0000' }, 57 | 'dc:creator': { '$t': 'thustetsuo' }, 58 | guid: 59 | { isPermaLink: 'true', 60 | '$t': 'http://agenceweboffshore.wordpress.com/2009/04/30/developpement-de-sites-web-offshore/' }, 61 | description: { '$cd': 'Il ya beaucoup d'agences web offshore de creation et conception du site Web des entreprises dans les pays d'Europe orientale et d'Asie du Sud-Est, la grande expérience dans […]' }, 62 | source: 63 | { url: 'http://agenceweboffshore.wordpress.com/feed/', 64 | '$t': 'Agence offshore de sous-traitance web' } }, 65 | { title: { '$cd': 'Referencement de site web' }, 66 | link: { '$t': 'http://netio.wordpress.com/2010/01/15/referencement-de-site-web/' }, 67 | pubDate: { '$t': 'Fri, 15 Jan 2010 18:21:47 +0000' }, 68 | 'dc:creator': { '$t': 'netio' }, 69 | guid: 70 | { isPermaLink: 'true', 71 | '$t': 'http://netio.wordpress.com/2010/01/15/referencement-de-site-web/' }, 72 | description: { '$cd': 'Le referencement d’un site web chez Netio\n\t\t\t\tLe referencement de site web avec Netio c’est l’assurance que votre site web se trouve dans les premieres pages des moteurs de […]' }, 73 | source: 74 | { url: 'http://netio.wordpress.com/feed/', 75 | '$t': 'Netio - CREATION SITE WEB' } } ] } } } -------------------------------------------------------------------------------- /examples/sample02.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Web - WordPress.com Search 5 | http://fr.search.wordpress.com/?q=Web 6 | Web - WordPress.com Search 7 | Wed, 22 Feb 2012 13:32:27 +0000 8 | fr 9 | 10 | http://s.wordpress.com/i/buttonw-com.png 11 | 224 12 | 58 13 | WordPress.com 14 | http://wordpress.com/ 15 | 16 | http://search.wordpress.com/ 17 | 18 | 65 | 66 | -------------------------------------------------------------------------------- /examples/xml-mirror: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // Usage : 4 | // 5 | // cat ./sample01.xml | ./xml-mirror | xmllint --format - | diff sample01.xml - 6 | // 7 | var buffer = ''; 8 | 9 | process.stdin.resume(); 10 | process.stdin.setEncoding('utf8'); 11 | 12 | process.stdin.on('data', function (chunk) { 13 | buffer += chunk; 14 | }); 15 | 16 | process.stdin.on('end', function () { 17 | var XMLMapping = require('../lib/xml-mapping.js'); 18 | process.stdout.write(XMLMapping.toxml(XMLMapping.tojson(buffer))); 19 | }); 20 | -------------------------------------------------------------------------------- /examples/xml2json: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // Usage : 4 | // 5 | // cat ./sample01.xml | ./xml2json 6 | // 7 | var buffer = ''; 8 | 9 | process.stdin.resume(); 10 | process.stdin.setEncoding('utf8'); 11 | 12 | process.stdin.on('data', function (chunk) { 13 | buffer += chunk; 14 | }); 15 | 16 | process.stdin.on('end', function () { 17 | var XMLMapping = require('../lib/xml-mapping.js'), util = require('util'); 18 | process.stdout.write(util.inspect(XMLMapping.tojson(buffer), false, null, false)); 19 | }); 20 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/xml-mapping.js'); 2 | -------------------------------------------------------------------------------- /lib/xml-mapping.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var XMLWriter = require('xml-writer'); 3 | var default_tag_name = 'row'; 4 | 5 | // STANDARDS 6 | var elementLongTag = 'element'; 7 | var elementShortTag = 'e'; 8 | var textLongTag = 'text'; 9 | var textShortTag = 't'; 10 | var commentLongTag = 'comment'; 11 | var commentShortTag = 'c'; 12 | var cdataLongTag = 'cdata'; 13 | var cdataShortTag = 'cd'; 14 | 15 | // NON-STANDARDS 16 | var orderLongTag = 'order'; 17 | var orderShortTag = 'o'; 18 | var lineLongTag = 'line'; 19 | var lineShortTag = 'y'; 20 | var columnLongTag = 'column'; 21 | var columnShortTag = 'x'; 22 | var nameLongTag = 'name'; 23 | var nameShortTag = 'n'; 24 | 25 | exports.dump = function (obj, options) { 26 | options = options || {}; 27 | options.specialChar = options.specialChar || '$'; 28 | 29 | if (typeof obj != "object") { 30 | return obj; 31 | } 32 | // if (typeof xw != XMLWriter) 33 | var xw = new XMLWriter(options.indent); 34 | if (options.header) { 35 | xw.startDocument(options.version || '1.0', options.encoding || 'utf-8'); 36 | } 37 | 38 | var getype = function (o) { 39 | if (!o) { 40 | return 'undefined'; 41 | } 42 | else if (Array.isArray(o)) { 43 | return 'array'; 44 | } 45 | else { 46 | return typeof o; 47 | } 48 | } 49 | 50 | var getval = function (o) { 51 | return '' + o; // !TODO 52 | } 53 | 54 | var getname = function (n) { 55 | return n.replace(options.specialChar, ':'); 56 | } 57 | 58 | var sortKeys = function (o) { 59 | var keys = []; 60 | for (var key in o) { 61 | if (Object.hasOwnProperty.call(o, key) && o[key] != null && o[key] !== undefined) { 62 | keys.push(key); 63 | } 64 | } 65 | function keyComparator(a, b) { 66 | var a0 = a.charAt(0); 67 | var b0 = b.charAt(0); 68 | var compA = typeof o[a] === 'object' ? 1 : -1; 69 | var compB = typeof o[b] === 'object' ? 1 : -1; 70 | if (compA - compB !== 0) { 71 | return compA - compB; 72 | } 73 | compA = a0 === options.specialChar || a0 === '#' ? 1 : -1; 74 | compB = b0 === options.specialChar || b0 === '#' ? 1 : -1; 75 | return compA - compB; 76 | } 77 | keys.sort(keyComparator); 78 | return keys; 79 | }; 80 | 81 | var parse = function (o) { 82 | if (getype(o) != 'object') { 83 | return; 84 | } 85 | var keys = sortKeys(o); 86 | var ELT = options.specialChar + elementLongTag; 87 | var EST = options.specialChar + elementShortTag; 88 | for (var index in keys) { 89 | var key = keys[index]; 90 | var val = o[key]; 91 | var type1 = getype(val); 92 | if (type1 == 'object') { 93 | xw.startElement(getname(key)); 94 | parse(val); 95 | xw.endElement(); 96 | } 97 | else if (type1 == 'array' && val.length <= 0) { 98 | xw.startElement(getname(key)).endElement(); 99 | } 100 | else if (type1 == 'array' && val.length > 0) { 101 | var type2 = getype(val[0]); 102 | if (type2 == 'object') { 103 | val.forEach(function (item, index) { 104 | if (key === ELT || key === EST || key == '#element') { 105 | parse(item); 106 | } 107 | else { 108 | xw.startElement(getname(key)); 109 | parse(item); 110 | xw.endElement(); 111 | } 112 | } 113 | ); 114 | } 115 | else { 116 | val.forEach(function (item, index) { 117 | if (key == ELT || key == EST || key == '#element') { 118 | xw.startCData().text(getval(item)).endCData(); 119 | } 120 | else { 121 | xw.startElement(getname(key)); 122 | xw.startCData().text(getval(item)).endCData(); 123 | xw.endElement(); 124 | } 125 | } 126 | ); 127 | } 128 | } 129 | else { 130 | var TLT = options.specialChar + textLongTag; 131 | var TST = options.specialChar + textShortTag; 132 | if (key === TLT || key === TST || key == '#text') { 133 | xw.text(getval(val)); 134 | } 135 | else if (key === options.specialChar + commentLongTag || key === options.specialChar + commentShortTag || key == '#comment') { 136 | xw.startComment().text(getval(val)).endComment(); 137 | } 138 | else if (key === options.specialChar + cdataLongTag || key === options.specialChar + cdataShortTag || key == '#cdata' || key == '#cd') { 139 | xw.startCData().text(getval(val)).endCData(); 140 | } 141 | else { 142 | xw.startAttribute(getname(key)).text(getval(val)).endAttribute(); 143 | } 144 | } 145 | } 146 | } 147 | var countRoots = function (o) { 148 | var count = 0; 149 | for (var key in o) { 150 | if (o.hasOwnProperty(key)) { 151 | // if (0 !== prop.indexOf(options.specialChar)) { 152 | if (key !== options.specialChar + commentLongTag && key !== options.specialChar + commentShortTag && key != '#comment') { 153 | count++; 154 | } 155 | } 156 | } 157 | return count; 158 | } 159 | var o = {}; 160 | if (countRoots(obj) == 1) { 161 | o = obj; 162 | } 163 | else { 164 | o[default_tag_name] = obj; 165 | } 166 | parse(o); 167 | 168 | return xw.toString(); 169 | 170 | }; 171 | exports.load = function (str, options) { 172 | options = options || {}; 173 | options.specialChar = options.specialChar || '$'; 174 | options.longTag = options.longTag || false; 175 | 176 | if (typeof str != "string") { 177 | if (options.throwErrors) { 178 | throw new Error("Input was " + (typeof str) + ", expected a string"); 179 | } 180 | return str; 181 | } 182 | var parser = require("sax").parser(true, {trim : true, xmlns : false, position: true}); 183 | var result = {}, 184 | stack = [], 185 | cdata = '', 186 | nested = { check : [], previous : '', memory : [], buffer: '', name: [] }, 187 | path = [], 188 | order = 0; 189 | 190 | function cvalue(n, v, isXmlNode) { 191 | n = n.replace(':', options.specialChar); 192 | var o = stack[stack.length - 1]; 193 | if (o === undefined) { 194 | o = {}; 195 | o[n] = v; 196 | return o[n]; 197 | } 198 | else if (o[n] === undefined) { 199 | if (isXmlNode && options.arrays && options.arrays.indexOf("/" + path.join("/")) !== -1) { 200 | // Create new array only if current item is a XML node, 201 | // meaning it is not text, cdata or comment 202 | o[n] = new Array(v); 203 | return o[n][0]; 204 | } 205 | else { 206 | o[n] = v; 207 | return o[n]; 208 | } 209 | } 210 | else if (!Array.isArray(o[n])) { 211 | var x = o[n]; 212 | o[n] = new Array(x, v); 213 | return o[n][1]; 214 | } 215 | else { 216 | var i = o[n].push(v); 217 | return o[n][i - 1]; 218 | } 219 | } 220 | function cattr(o) { 221 | var r = {}; 222 | for (var key in o) { 223 | if (o.hasOwnProperty(key) && o[key]) { 224 | r[key.replace(':', options.specialChar)] = o[key]; 225 | } 226 | } 227 | return r; 228 | } 229 | 230 | 231 | if (!options.throwErrors) { 232 | parser.onerror = function (e) { 233 | // an error happened. 234 | }; 235 | } 236 | parser.onprocessinginstruction = function (pi) { 237 | }; 238 | parser.ontext = function (v) { 239 | if (options.nested) { 240 | if (nested.previous === 'C') { 241 | nested.check[nested.check.length - 1] = true; 242 | } 243 | nested.buffer = v; 244 | nested.memory.map(function (item) { return item.text(v) }); 245 | nested.previous = 'T'; 246 | } 247 | else { 248 | cvalue(options.specialChar + (options.longTag ? textLongTag : textShortTag), v); 249 | } 250 | }; 251 | parser.oncomment = function (v) { 252 | if (options.comments === undefined || options.comments !== false) { 253 | cvalue(options.specialChar + (options.longTag ? commentLongTag : commentShortTag), v); 254 | } 255 | }; 256 | parser.oncdata = function (v) { 257 | cdata += v; 258 | }; 259 | parser.onopencdata = function () { 260 | cdata = ''; 261 | }; 262 | parser.onclosecdata = function () { 263 | cvalue(options.specialChar + (options.longTag ? cdataLongTag : cdataShortTag), cdata); 264 | cdata = ''; 265 | }; 266 | parser.onopentag = function (node) { 267 | if (options.arrays) { 268 | path.push(node.name); 269 | } 270 | if (options.parserInfos) { 271 | order++; 272 | node.attributes[options.specialChar + (options.longTag ? orderLongTag : orderShortTag)] = order; 273 | node.attributes[options.specialChar + (options.longTag ? lineLongTag : lineShortTag)] = parser.line + 1; 274 | node.attributes[options.specialChar + (options.longTag ? columnLongTag : columnShortTag)] = parser.startTagPosition; 275 | node.attributes[options.specialChar + (options.longTag ? nameLongTag : nameShortTag)] = node.name; 276 | } 277 | if (options.nested) { 278 | if (nested.previous === 'T') { 279 | nested.check[nested.check.length - 1] = true; 280 | } 281 | nested.buffer = ''; 282 | nested.name.push(node.name); 283 | nested.memory.push(new XMLWriter()); 284 | nested.check.push(false); 285 | nested.previous = 'O'; 286 | 287 | nested.memory.map(function(item) { 288 | return Object.keys(node.attributes).reduce(function(prev, cur) { 289 | return prev.writeAttribute(cur.toString(), node.attributes[cur].toString()) 290 | }, item.startElement(node.name)) 291 | }); 292 | } 293 | 294 | if (stack.length == 1 && node.name == default_tag_name) { 295 | result = cattr(node.attributes); 296 | stack.push(result); 297 | } 298 | else { 299 | stack.push(cvalue(node.name, cattr(node.attributes), true)); 300 | } 301 | }; 302 | parser.onclosetag = function () { 303 | if (options.arrays) { 304 | path.pop(); 305 | } 306 | if (options.nested) { 307 | nested.memory.map(function(item) { return item.endElement() }); 308 | var ne = nested.name.pop(); 309 | var xv = nested.memory.pop(); 310 | if (nested.check.pop() === true) { 311 | var r1 = RegExp('(^<' + ne + '[^>]*>)', 'g'), 312 | r2 = RegExp('(]*>$)', 'g'); 313 | cvalue(options.specialChar + (options.longTag ? textLongTag : textShortTag), xv.toString().replace(r1, '').replace(r2, '')); 314 | } 315 | else { 316 | if (nested.buffer) { 317 | cvalue(options.specialChar + (options.longTag ? textLongTag : textShortTag), nested.buffer); 318 | } 319 | } 320 | xv = null; 321 | nested.buffer = ''; 322 | nested.previous = 'C'; 323 | } 324 | 325 | stack.pop(); 326 | }; 327 | parser.onready = function () { 328 | order = 0; 329 | }; 330 | parser.onend = function () { 331 | }; 332 | 333 | if (options.throwErrors) { 334 | stack.push(result); 335 | parser.write(str); 336 | var line = parser.line; 337 | var column = parser.column; 338 | parser.close(); 339 | stack.pop(); 340 | 341 | if (stack.length !== 0) { 342 | var er = "Unexpected end of input"; 343 | er += "\nLine: " + line + 344 | "\nColumn: " + column + 345 | "\nChar: null"; 346 | throw new Error(er); 347 | } 348 | } else { 349 | stack.push(result); 350 | try { 351 | parser.write(str).close(); 352 | } 353 | catch (e) { 354 | return str; 355 | } 356 | stack.pop(); 357 | } 358 | 359 | return result; 360 | } 361 | 362 | exports.toxml = exports.dump; 363 | exports.tojson = exports.load; 364 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xml-mapping", 3 | "version": "1.7.1", 4 | "author": "Nicolas Thouvenin ", 5 | "contributors": [ 6 | { 7 | "name": "Joe Ibershoff", 8 | "url": "https://github.com/zacronos", 9 | "email": "joe@ibershoff.com" 10 | }, 11 | { 12 | "name": "Yura Zenevich", 13 | "url": "https://github.com/yzen", 14 | "email": "yura.zenevich@gmail.com" 15 | }, 16 | { 17 | "name": "Thorsten Lorenz", 18 | "url": "https://github.com/thlorenz", 19 | "email": "thlorenz@gmx.de" 20 | }, 21 | { 22 | "name": "ntgn81", 23 | "url": "https://github.com/ntgn81", 24 | "email": "ntgn81@gmail.com" 25 | }, 26 | { 27 | "name": "Rowell Cruz", 28 | "url": "https://github.com/rcruz" 29 | }, 30 | { 31 | "name": "JT", 32 | "url": "https://github.com/spikyjt" 33 | } 34 | ], 35 | "description": "provide a bidirectionnal mapping between XML and JS data Structure (aka JSON)", 36 | "keywords": [ 37 | "xml", 38 | "json", 39 | "json2xml", 40 | "xml2json" 41 | ], 42 | "homepage": "http://github.com/touv/node-xml-mapping", 43 | "scripts": { 44 | "test": "nodeunit test/" 45 | }, 46 | "main": "./xml-mapping.js", 47 | "repository": { 48 | "type": "git", 49 | "url": "https://github.com/touv/node-xml-mapping.git" 50 | }, 51 | "dependencies": { 52 | "xml-writer": ">=1.0.4", 53 | "sax": "=0.4.2" 54 | }, 55 | "devDependencies": { 56 | "nodeunit": ">=0.7.3" 57 | }, 58 | "license": "MIT", 59 | "engines": { 60 | "node": ">=0.2.0" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /test/basic.js: -------------------------------------------------------------------------------- 1 | exports['test'] = function (test) { 2 | test.done(); 3 | }; 4 | -------------------------------------------------------------------------------- /test/nested.js: -------------------------------------------------------------------------------- 1 | var XMLMapping = require('../'); 2 | 3 | exports['t01'] = function (test) { 4 | input = 'aaa'; 5 | test.deepEqual(XMLMapping.load(input, {nested : true, throwErrors: true}), { key : { $t : 'aaa'} }); 6 | test.done(); 7 | }; 8 | exports['t02'] = function (test) { 9 | input = 'aaabbbccc'; 10 | test.deepEqual(XMLMapping.load(input, {nested : true, throwErrors: true}), { key : { $t : 'aaabbbccc', em: { '$t': 'bbb' }} }); 11 | test.done(); 12 | }; 13 | exports['t03'] = function (test) { 14 | input = 'bbbccc'; 15 | test.deepEqual(XMLMapping.load(input, {nested : true, throwErrors: true}), { key : { $t : 'bbbccc', em: { '$t': 'bbb' } } }); 16 | test.done(); 17 | }; 18 | exports['t04'] = function (test) { 19 | input = 'aaabbb'; 20 | test.deepEqual(XMLMapping.load(input, {nested : true, throwErrors: true}), { key : { $t : 'aaabbb', em: { '$t': 'bbb' }} }); 21 | test.done(); 22 | }; 23 | exports['t05'] = function (test) { 24 | input = 'bbb'; 25 | test.deepEqual(XMLMapping.load(input, {nested : true, throwErrors: true}), { key : { em : { $t : 'bbb'} } }); 26 | test.done(); 27 | }; 28 | exports['t06'] = function (test) { 29 | input = 'aaa
ccc
'; 30 | test.deepEqual(XMLMapping.load(input, {nested : true, throwErrors: true}), { key : { $t : 'aaa
ccc', br: { } } }); 31 | test.done(); 32 | }; 33 | exports['t07'] = function (test) { 34 | input = '
ccc
'; 35 | test.deepEqual(XMLMapping.load(input, {nested : true, throwErrors: true}), { key : { $t : '
ccc', br: { } } }); 36 | test.done(); 37 | }; 38 | exports['t08'] = function (test) { 39 | input = 'aaa
'; 40 | test.deepEqual(XMLMapping.load(input, {nested : true, throwErrors: true}), { key : { $t : 'aaa
', br: { } } }); 41 | test.done(); 42 | }; 43 | exports['t09'] = function (test) { 44 | input = '
'; 45 | test.deepEqual(XMLMapping.load(input, {nested : true, throwErrors: true}), { key : { br : { } } }); 46 | test.done(); 47 | }; 48 | exports['t11'] = function (test) { 49 | input = 'aaa'; 50 | test.deepEqual(XMLMapping.load(input, {nested : true, throwErrors: true}), { key : { $t : 'aaa'} }); 51 | test.done(); 52 | }; 53 | exports['t12'] = function (test) { 54 | input = 'aaabbbccc'; 55 | test.deepEqual(XMLMapping.load(input, {nested : true, throwErrors: true}), { key : { $t : 'aaabbbccc', em: { '$t': 'bbb', attr: 'xxx' }} }); 56 | test.done(); 57 | }; 58 | exports['t13'] = function (test) { 59 | input = 'bbbccc'; 60 | test.deepEqual(XMLMapping.load(input, {nested : true, throwErrors: true}), { key : { $t : 'bbbccc', em: { '$t': 'bbb', attr: 'xxx' } } }); 61 | test.done(); 62 | }; 63 | exports['t14'] = function (test) { 64 | input = 'aaabbb'; 65 | test.deepEqual(XMLMapping.load(input, {nested : true, throwErrors: true}), { key : { $t : 'aaabbb', em: { '$t': 'bbb', attr: 'xxx' }} }); 66 | test.done(); 67 | }; 68 | exports['t15'] = function (test) { 69 | input = 'bbb'; 70 | test.deepEqual(XMLMapping.load(input, {nested : true, throwErrors: true}), { key : { em : { $t : 'bbb', attr: 'xxx' } } }); 71 | test.done(); 72 | }; 73 | exports['t16'] = function (test) { 74 | input = 'aaa
ccc
'; 75 | test.deepEqual(XMLMapping.load(input, {nested : true, throwErrors: true}), { key : { $t : 'aaa
ccc', br: { attr: 'xxx' } } }); 76 | test.done(); 77 | }; 78 | exports['t17'] = function (test) { 79 | input = '
ccc
'; 80 | test.deepEqual(XMLMapping.load(input, {nested : true, throwErrors: true}), { key : { $t : '
ccc', br: { attr: 'xxx' } } }); 81 | test.done(); 82 | }; 83 | exports['t18'] = function (test) { 84 | input = 'aaa
'; 85 | test.deepEqual(XMLMapping.load(input, {nested : true, throwErrors: true}), { key : { $t : 'aaa
', br: { attr: 'xxx' } } }); 86 | test.done(); 87 | }; 88 | exports['t19'] = function (test) { 89 | input = '
'; 90 | test.deepEqual(XMLMapping.load(input, {nested : true, throwErrors: true}), { key : { br : { attr: 'xxx' } } }); 91 | test.done(); 92 | }; 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /test/parserinfos.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var XMLMapping = require('../'); 3 | 4 | exports['t01'] = function (test) { 5 | var options = { 6 | nested : true, 7 | throwErrors: true, 8 | parserInfos: true 9 | }; 10 | var input = '123'; 11 | var expected = { 12 | root: 13 | { '$o': 1, 14 | '$y': 1, 15 | '$x': 1, 16 | '$n': 'root', 17 | a: [ { '$o': 2, 18 | '$y': 1, 19 | '$x': 7, 20 | '$n': 'a', 21 | '$t': '1' }, 22 | { '$o': 4, 23 | '$y': 1, 24 | '$x': 23, 25 | '$n': 'a', 26 | '$t': '3' } ], 27 | b: { '$o': 3, 28 | '$y': 1, 29 | '$x': 15, 30 | '$n': 'b', 31 | '$t': '2' 32 | } 33 | } 34 | }; 35 | var actual = XMLMapping.load(input, options); 36 | test.deepEqual(actual, expected); 37 | test.done(); 38 | }; 39 | 40 | -------------------------------------------------------------------------------- /test/tojson.js: -------------------------------------------------------------------------------- 1 | var XMLMapping = require('../'); 2 | var input; 3 | exports['t00'] = function (test) { 4 | input = ''; 5 | test.deepEqual(XMLMapping.load(input), {}); 6 | input = 'string'; 7 | test.equal(XMLMapping.load(input), 'string'); 8 | input = 1234; 9 | test.equal(XMLMapping.load(input), 1234); 10 | test.done(); 11 | }; 12 | exports['t01'] = function (test) { 13 | input = ''; 14 | test.deepEqual(XMLMapping.load(input), { key : {} }); 15 | input = ''; 16 | test.deepEqual(XMLMapping.load(input), { key : { key1: 'value' } }); 17 | input = ''; 18 | test.deepEqual(XMLMapping.load(input), { key : { key1: 'value1', key2: 'value2' } }); 19 | test.done(); 20 | }; 21 | exports['t02'] = function (test) { 22 | input = ''; 23 | test.deepEqual(XMLMapping.load(input), { key1 : {}, key2 : {} }); 24 | input = ''; 25 | test.deepEqual(XMLMapping.load(input), { key1 : { key: 'value' }, key2 : { key: 'value' } }); 26 | input = ''; 27 | test.deepEqual(XMLMapping.load(input), { key1 : { keyA: 'value1', keyB: 'value2' }, key2 : { keyA: 'value1', keyB: 'value2' } }); 28 | input = ''; 29 | test.deepEqual(XMLMapping.load(input), { key1 : { keyA: 'value1', keyB: 'value2', keyC: 'value3' }, key2 : { keyA: 'value1', keyB: 'value2', keyC: 'value3' } }); 30 | test.done(); 31 | }; 32 | 33 | exports['t03a'] = function (test) { 34 | input = ''; 35 | test.deepEqual(XMLMapping.load(input), { key : [] }); 36 | test.done(); 37 | } 38 | exports['t03b'] = function (test) { 39 | input = ''; 40 | test.deepEqual(XMLMapping.load(input), { key : [{},{}] }); 41 | input = ''; 42 | test.deepEqual(XMLMapping.load(input), { key : [{},{},{}] }); 43 | test.done(); 44 | } 45 | exports['t03c'] = function (test) { 46 | input = 'value1value2'; 47 | test.deepEqual(XMLMapping.load(input), { key : [{ $t : 'value1'}, { $t : 'value2'}] }); 48 | input = 'value1value2value3'; 49 | test.deepEqual(XMLMapping.load(input), { key : [{ $t : 'value1'}, { $t : 'value2'}, { $t : 'value3'}] }); 50 | test.done(); 51 | }; 52 | exports['t03d'] = function (test) { 53 | input = ''; 54 | test.deepEqual(XMLMapping.load(input), { key : { $c : 'value1'} }); 55 | input = ''; 56 | test.deepEqual(XMLMapping.load(input), { key : { $c : ['value1','value2'] } }); 57 | input = ''; 58 | test.deepEqual(XMLMapping.load(input), { key : [{ $c : 'value1'}, { $c : 'value2'}] }); 59 | input = ''; 60 | test.deepEqual(XMLMapping.load(input), { key : [{ $c : 'value1'}, { $c : 'value2'}, { $c : 'value3'}] }); 61 | test.done(); 62 | }; 63 | exports['t03e'] = function (test) { 64 | input = ''; 65 | test.deepEqual(XMLMapping.load(input), { key : { $cd : 'value1'} }); 66 | input = ''; 67 | test.deepEqual(XMLMapping.load(input), { key : { $cd : ['value1', 'value2']} }); 68 | input = ''; 69 | test.deepEqual(XMLMapping.load(input), { key1 : { key2 : { $cd : 'value1'}, key3 : { $cd : 'value2'} } }); 70 | input = ''; 71 | test.deepEqual(XMLMapping.load(input), { key : [{ $cd : 'value1'}, { $cd : 'value2'}, { $cd : 'value3'}] }); 72 | test.done(); 73 | }; 74 | exports['t04'] = function (test) { 75 | input = '\nvalue2'; 76 | test.deepEqual(XMLMapping.load(input), { key1 : { key2 : 'value1', key3 : { $t : 'value2'} } }); 77 | input = 'value2'; 78 | test.deepEqual(XMLMapping.load(input), { key1 : { key2 : 'value1', key3 : { $t : 'value2'} } }); 79 | input = 'value2'; 80 | test.deepEqual(XMLMapping.load(input), { key1 : { key2 : 'value1', key3 : { key4 : { $t : 'value2'} } } }); 81 | input = 'value2'; 82 | test.deepEqual(XMLMapping.load(input), { key1 : { key2 : 'value1', key3 : { key4 : { key5 : { $t : 'value2'} } } } }); 83 | input = 'value'; 84 | test.deepEqual(XMLMapping.load(input), { key1 : { key2 : { key3 : 'value', key4 : { key5 : 'value', key6 : 'value', key7 : { key8 : 'value', key9 : 'value', key10 : 'value', $t : 'value' } } } } }); 85 | test.done(); 86 | }; 87 | exports['t05'] = function (test) { 88 | input = 'value1value2'; 89 | test.deepEqual(XMLMapping.load(input), { key1 : { key2 : { $t : 'value1'}, key3 : { $t : 'value2'} } }); 90 | input = 'value2value3'; 91 | test.deepEqual(XMLMapping.load(input), { key1 : { key2 : 'value1', key3 : { $t : 'value2'} , key4 : { $t : 'value3'} } }); 92 | test.done(); 93 | }; 94 | 95 | exports['t06a'] = function (test) { 96 | input = {}; 97 | var caughtError = false; 98 | try { 99 | XMLMapping.load(input); 100 | } catch (err) { 101 | caughtError = true; 102 | } 103 | test.equal(caughtError, false); 104 | try { 105 | XMLMapping.load(input, {throwErrors: true}); 106 | } catch (err) { 107 | caughtError = true; 108 | } 109 | test.equal(caughtError, true); 110 | test.done(); 111 | }; 112 | 113 | exports['t06b'] = function (test) { 114 | input = 'value1value2') 7 | input = 'string'; 8 | test.equal(XMLMapping.dump(input), 'string') 9 | input = 1234; 10 | test.equal(XMLMapping.dump(input), 1234) 11 | test.done(); 12 | }; 13 | exports['t01'] = function (test) { 14 | input = { 15 | key : {} 16 | }; 17 | test.equal(XMLMapping.dump(input), ''); 18 | input = { 19 | key : { 20 | key1: 'value' 21 | } 22 | }; 23 | test.equal(XMLMapping.dump(input), ''); 24 | input = { 25 | key : { 26 | key1: 'value1', 27 | key2: 'value2' 28 | } 29 | }; 30 | test.equal(XMLMapping.dump(input), ''); 31 | test.done(); 32 | }; 33 | exports['t02'] = function (test) { 34 | input = { 35 | key1 : {}, 36 | key2 : {} 37 | }; 38 | test.equal(XMLMapping.dump(input), ''); 39 | input = { 40 | key1 : { 41 | key: 'value' 42 | }, 43 | key2 : { 44 | key: 'value' 45 | } 46 | }; 47 | test.equal(XMLMapping.dump(input), ''); 48 | input = { 49 | key1 : { 50 | keyA: 'value1', 51 | keyB: 'value2' 52 | }, 53 | key2 : { 54 | keyA: 'value1', 55 | keyB: 'value2' 56 | } 57 | }; 58 | test.equal(XMLMapping.dump(input), ''); 59 | test.done(); 60 | }; 61 | exports['t03a'] = function (test) { 62 | input = { 63 | key : [] 64 | }; 65 | test.equal(XMLMapping.dump(input), ''); 66 | test.done(); 67 | } 68 | exports['t03b'] = function (test) { 69 | input = { 70 | key : [{},{}] 71 | }; 72 | test.equal(XMLMapping.dump(input), ''); 73 | test.done(); 74 | } 75 | exports['t03c'] = function (test) { 76 | input = { 77 | key : [{ $t : 'value'}, { $text : 'value'}, { '#text' : 'value'}] 78 | }; 79 | test.equal(XMLMapping.dump(input), 'valuevaluevalue'); 80 | test.done(); 81 | }; 82 | exports['t03d'] = function (test) { 83 | input = { 84 | key : [{ $c : 'value'}, { '#comment' : 'value'}, { '$comment' : 'value'}] 85 | }; 86 | test.equal(XMLMapping.dump(input), ''); 87 | test.done(); 88 | }; 89 | exports['t03e'] = function (test) { 90 | input = { 91 | key : [{ $cd : 'value'}, { '#cdata' : 'value'}, { '$cdata' : 'value'}] 92 | }; 93 | test.equal(XMLMapping.dump(input), ''); 94 | test.done(); 95 | }; 96 | 97 | 98 | exports['t04a'] = function (test) { 99 | input = { 100 | $t : 'value' 101 | }; 102 | test.equal(XMLMapping.dump(input), ''); 103 | test.done(); 104 | }; 105 | exports['t04b'] = function (test) { 106 | input = { 107 | key: ['am', 'stram', 'dram'] 108 | }; 109 | test.equal(XMLMapping.dump(input), ''); 110 | test.done(); 111 | }; 112 | exports['t05a'] = function (test) { 113 | input = { 114 | '#element' : [{ $cd : 'value'}, { '#cd' : 'value'}] 115 | }; 116 | test.equal(XMLMapping.dump(input), ''); 117 | test.done(); 118 | }; 119 | exports['t05b'] = function (test) { 120 | input = { 121 | key : { 122 | '#element' : [{ $t : 'amstra'}, { _t : 'mdram'}] 123 | } 124 | }; 125 | test.equal(XMLMapping.dump(input), 'amstramdram'); 126 | test.done(); 127 | }; 128 | exports['t06'] = function (test) { 129 | input = { 130 | key : { 131 | '$t' : 1 132 | } 133 | }; 134 | test.equal(XMLMapping.dump(input), '1'); 135 | input = { 136 | key : { 137 | '$t' : 0 138 | } 139 | }; 140 | test.equal(XMLMapping.dump(input), '0'); 141 | test.done(); 142 | }; 143 | exports['t07'] = function (test) { 144 | input = { 145 | key: { 146 | $t: "value", 147 | arg: "arg" 148 | } 149 | }; 150 | test.equal(XMLMapping.dump(input), 'value'); 151 | test.done(); 152 | }; 153 | exports['t08'] = function (test) { 154 | input = { 155 | key: { 156 | a: "a", 157 | val: { 158 | $t: "val" 159 | }, 160 | c: "c" 161 | } 162 | }; 163 | test.equal(XMLMapping.dump(input), 'val'); 164 | test.done(); 165 | }; 166 | exports['t09'] = function (test) { 167 | input = { 168 | key: { 169 | a: "a", 170 | val: { 171 | $t: "val" 172 | }, 173 | c: "c" 174 | } 175 | }; 176 | test.equal(XMLMapping.dump(input, { indent: true }), '\n val\n'); 177 | test.done(); 178 | }; 179 | exports['t10a'] = function (test) { 180 | input = { 181 | '$c' : ' comment ', 182 | key1 : { 183 | key2 : { 184 | '$cd' : 'value1' 185 | }, 186 | key3 : { 187 | '$t' : 'value2' 188 | } 189 | } 190 | }; 191 | output = 'value2'; 192 | test.equal(XMLMapping.dump(input), output); 193 | test.done(); 194 | } 195 | exports['t10b'] = function (test) { 196 | input = { 197 | '$c' : ' comment ', 198 | key1 : { 199 | key2 : { 200 | '$cd' : 'value1' 201 | }, 202 | key3 : { 203 | '$t' : 'value2' 204 | } 205 | } 206 | }; 207 | output = '\n\n\n \n \n value2\n'; 208 | test.equal(XMLMapping.dump(input, {header:true, version: '1.0', encoding:'UTF-8', indent: true}), output); 209 | test.done(); 210 | } 211 | --------------------------------------------------------------------------------