Giter Site home page Giter Site logo

nashwaan / xml-js Goto Github PK

View Code? Open in Web Editor NEW
1.3K 17.0 181.0 3.68 MB

Converter utility between XML text and Javascript object / JSON text.

License: MIT License

JavaScript 97.29% HTML 0.88% TypeScript 0.39% CSS 1.43%
json-xml convert-js json-converter xml-js cdata convertor parser xml xml-parser json

xml-js's Introduction

XML ⇔ JS/JSON

Convert XML text to Javascript object / JSON text (and vice versa).

Build Status Build Status Build Status

Coverage Status codecov Codacy Badge Code Climate

npm License Downloads/month Dependency Status Package Quality

Synopsis

Convert XML ↔ JS/JSON as compact or non-compact

Features

  • Maintain Order of Elements: Most libraries will convert <a/><b/><a/> to {a:[{},{}],b:{}} which merges any node of same name into an array. This library can create the following to preserve the order of elements: {"elements":[{"type":"element","name":"a"},{"type":"element","name":"b"},{"type":"element","name":"a"}]}.

This is very important and it is the main reason why this library was created. Read also Compact vs Non-Compact for more info.

  • Fully XML Compliant: Can parse: elements, attributes, texts, comments, CData, DOCTYPE, XML declarations, and Processing Instructions.

  • Reversible: Whether converting xml→json or json→xml, the result can be converted back to its original form.

  • Minimal Dependencies: This library depends only on one external npm module.

  • Change Property Key Name: Usually output of XML attributes are stored in @attr, _atrr, $attr or $ in order to avoid conflicting with name of sub-elements. This library store them in attributes, but most importantly, you can change this to whatever you like.

  • Support Upwards Traversal: By setting {addParent: true} option, an extra property named parent will be generated along each element so that its parent can be referenced. Therefore, anywhere during the traversal of an element, its children and its parent can be easily accessed.

  • Support Command Line: To quickly convert xml or json files, this module can be installed globally or locally (i.e. use it as script in package.json).

  • Customize Processing using Callback Hooks: Custom functions can be supplied to do additional processing for different parts of xml or json (like cdata, comments, elements, attributes ...etc).

  • Portable Code: Written purely in JavaScript which means it can be used in Node environment and browser environment (via bundlers like browserify/JSPM/Webpack).

  • Typings Info Included: Support type checking and code suggestion via intellisense. Thanks to the wonderful efforts by DenisCarriere.

Compact vs Non-Compact

Most XML to JSON converters (including online converters) convert <a/> to some compact output like {"a":{}} instead of non-compact output like {"elements":[{"type":"element","name":"a"}]}.

While compact output might work in most situations, there are cases when elements of different names are mixed inside a parent element. Lets use <a x="1"/><b x="2"/><a x="3"/> as an example. Most converters will produce compact output like this {a:[{_:{x:"1"}},{_:{x:"3"}}], b:{_:{x:"2"}}}, which has merged both <a> elements into an array. If you try to convert this back to xml, you will get <a x="1"/><a x="3"/><b x="2"/> which has not preserved the order of elements!

The reason behind this behavior is due to the inherent limitation in the compact representation. Because output like {a:{_:{x:"1"}}, b:{_:{x:"2"}}, a:{_:{x:"3"}}} is illegal (same property name a should not appear twice in an object). This leaves no option but to use array {a:[{_:{x:"1"}},{_:{x:"3"}}].

The non-compact output, which is supported by this library, will produce more information and always guarantees the order of the elements as they appeared in the XML file.

Another drawback of compact output is the resultant element can be an object or an array and therefore makes the client code a little awkward in terms of the extra check needed on object type before processing.

NOTE: Although non-compact output is more accurate representation of original XML than compact version, the non-compact version is verbose and consumes more space. This library provides both options. Use {compact: false} if you are not sure because it preserves everything; otherwise use {compact: true} if you want to save space and you don't care about mixing elements of same name and losing their order.

Tip: You can reduce the output size by using shorter key names.

Usage

Installation

npm install --save xml-js

You can also install it globally to use it as a command line convertor (see Command Line).

npm install --global xml-js

Quick start

var convert = require('xml-js');
var xml =
'<?xml version="1.0" encoding="utf-8"?>' +
'<note importance="high" logged="true">' +
'    <title>Happy</title>' +
'    <todo>Work</todo>' +
'    <todo>Play</todo>' +
'</note>';
var result1 = convert.xml2json(xml, {compact: true, spaces: 4});
var result2 = convert.xml2json(xml, {compact: false, spaces: 4});
console.log(result1, '\n', result2);

To see the result of this code, see the output above in Synopsis section.

Or run and edit this code live in the browser.

Sample Conversions

XML JS/JSON compact JS/JSON non-compact
<a/> {"a":{}} {"elements":[{"type":"element","name":"a"}]}
<a/><b/> {"a":{},"b":{}} {"elements":[{"type":"element","name":"a"},{"type":"element","name":"b"}]}
<a><b/></a> {"a":{"b":{}}} {"elements":[{"type":"element","name":"a","elements":[{"type":"element","name":"b"}]}]}
<a> Hi </a> {"a":{"_text":" Hi "}} {"elements":[{"type":"element","name":"a","elements":[{"type":"text","text":" Hi "}]}]}
<a x="1.234" y="It's"/> {"a":{"_attributes":{"x":"1.234","y":"It's"}}} {"elements":[{"type":"element","name":"a","attributes":{"x":"1.234","y":"It's"}}]}
<?xml?> {"_declaration":{}} {"declaration":{}}
<?go there?> {"_instruction":{"go":"there"}} {"elements":[{"type":"instruction","name":"go","instruction":"there"}]}
<?xml version="1.0" encoding="utf-8"?> {"_declaration":{"_attributes":{"version":"1.0","encoding":"utf-8"}}} {"declaration":{"attributes":{"version":"1.0","encoding":"utf-8"}}}
<!--Hello, World!--> {"_comment":"Hello, World!"} {"elements":[{"type":"comment","comment":"Hello, World!"}]}
<![CDATA[<foo></bar>]]> {"_cdata":"<foo></bar>"} {"elements":[{"type":"cdata","cdata":"<foo></bar>"}]}

API Reference

This library provides 4 functions: js2xml(), json2xml(), xml2js(), and xml2json(). Here are the usages for each one (see more details in the following sections):

var convert = require('xml-js');
result = convert.js2xml(js, options);     // to convert javascript object to xml text
result = convert.json2xml(json, options); // to convert json text to xml text
result = convert.xml2js(xml, options);    // to convert xml text to javascript object
result = convert.xml2json(xml, options);  // to convert xml text to json text

Convert JS object / JSON → XML

To convert JavaScript object to XML text, use js2xml(). To convert JSON text to XML text, use json2xml().

var convert = require('xml-js');
var json = require('fs').readFileSync('test.json', 'utf8');
var options = {compact: true, ignoreComment: true, spaces: 4};
var result = convert.json2xml(json, options);
console.log(result);

Options for Converting JS object / JSON → XML

The below options are applicable for both js2xml() and json2xml() functions.

Option Default Description
spaces 0 Number of spaces to be used for indenting XML output. Passing characters like ' ' or '\t' are also accepted.
compact false Whether the input object is in compact form or not. By default, input is expected to be in non-compact form.
IMPORTANT: Remeber to set this option compact: true if you are supplying normal json (which is likely equivalent to compact form). Otherwise, the function assumes your json input is non-compact form and you will not get a result if it is not in that form. See Synopsis to know the difference between the two json forms
fullTagEmptyElement false Whether to produce element without sub-elements as full tag pairs <a></a> rather than self closing tag <a/>.
indentCdata false Whether to write CData in a new line and indent it. Will generate <a>\n <![CDATA[foo]]></a> instead of <a><![CDATA[foo]]></a>. See discussion
indentAttributes false Whether to print attributes across multiple lines and indent them (when spaces is not 0). See example.
ignoreDeclaration false Whether to ignore writing declaration directives of xml. For example, <?xml?> will be ignored.
ignoreInstruction false Whether to ignore writing processing instruction of xml. For example, <?go there?> will be ignored.
ignoreAttributes false Whether to ignore writing attributes of the elements. For example, x="1" in <a x="1"></a> will be ignored
ignoreComment false Whether to ignore writing comments of the elements. That is, no <!-- --> will be generated.
ignoreCdata false Whether to ignore writing CData of the elements. That is, no <![CDATA[ ]]> will be generated.
ignoreDoctype false Whether to ignore writing Doctype of the elements. That is, no <!DOCTYPE > will be generated.
ignoreText false Whether to ignore writing texts of the elements. For example, hi text in <a>hi</a> will be ignored.

Convert XML → JS object / JSON

To convert XML text to JavaScript object, use xml2js(). To convert XML text to JSON text, use xml2json().

var convert = require('xml-js');
var xml = require('fs').readFileSync('test.xml', 'utf8');
var options = {ignoreComment: true, alwaysChildren: true};
var result = convert.xml2js(xml, options); // or convert.xml2json(xml, options)
console.log(result);

Options for Converting XML → JS object / JSON

The below options are applicable for both xml2js() and xml2json() functions.

Option Default Description
compact false Whether to produce detailed object or compact object.
trim false Whether to trim whitespace characters that may exist before and after the text.
sanitize (Deprecated) false Whether to replace & < > with &amp; &lt; &gt; respectively, in the resultant text.
nativeType false Whether to attempt converting text of numerals or of boolean values to native type. For example, "123" will be 123 and "true" will be true
nativeTypeAttributes false Whether to attempt converting attributes of numerals or of boolean values to native type. See also nativeType above.
addParent false Whether to add parent property in each element object that points to parent object.
alwaysArray false Whether to always put sub element, even if it is one only, as an item inside an array. <a><b/></a> will be a:[{b:[{}]}] rather than a:{b:{}} (applicable for compact output only). If the passed value is an array, only elements with names in the passed array are always made arrays.
alwaysChildren false Whether to always generate elements property even when there are no actual sub elements. <a></a> will be {"elements":[{"type":"element","name":"a","elements":[]}]} rather than {"elements":[{"type":"element","name":"a"}]} (applicable for non-compact output).
instructionHasAttributes false Whether to parse contents of Processing Instruction as attributes or not. <?go to="there"?> will be {"_instruction":{"go":{"_attributes":{"to":"there"}}}} rather than {"_instruction":{"go":"to=\"there\""}}. See discussion.
ignoreDeclaration false Whether to ignore parsing declaration property. That is, no declaration property will be generated.
ignoreInstruction false Whether to ignore parsing processing instruction property. That is, no instruction property will be generated.
ignoreAttributes false Whether to ignore parsing attributes of elements.That is, no attributes property will be generated.
ignoreComment false Whether to ignore parsing comments of the elements. That is, no comment will be generated.
ignoreCdata false Whether to ignore parsing CData of the elements. That is, no cdata will be generated.
ignoreDoctype false Whether to ignore parsing Doctype of the elements. That is, no doctype will be generated.
ignoreText false Whether to ignore parsing texts of the elements. That is, no text will be generated.

The below option is applicable only for xml2json() function.

Option Default Description
spaces 0 Number of spaces to be used for indenting JSON output. Passing characters like ' ' or '\t' are also accepted.

Options for Changing Key Names

To change default key names in the output object or the default key names assumed in the input JavaScript object / JSON, use the following options:

Option Default Description
declarationKey "declaration" or "_declaration" Name of the property key which will be used for the declaration. For example, if declarationKey: '$declaration' then output of <?xml?> will be {"$declaration":{}} (in compact form)
instructionKey "instruction" or "_instruction" Name of the property key which will be used for the processing instruction. For example, if instructionKey: '$instruction' then output of <?go there?> will be {"$instruction":{"go":"there"}} (in compact form)
attributesKey "attributes" or "_attributes" Name of the property key which will be used for the attributes. For example, if attributesKey: '$attributes' then output of <a x="hello"/> will be {"a":{$attributes:{"x":"hello"}}} (in compact form)
textKey "text" or "_text" Name of the property key which will be used for the text. For example, if textKey: '$text' then output of <a>hi</a> will be {"a":{"$text":"Hi"}} (in compact form)
cdataKey "cdata" or "_cdata" Name of the property key which will be used for the cdata. For example, if cdataKey: '$cdata' then output of <![CDATA[1 is < 2]]> will be {"$cdata":"1 is < 2"} (in compact form)
doctypeKey "doctype" or "_doctype" Name of the property key which will be used for the doctype. For example, if doctypeKey: '$doctype' then output of <!DOCTYPE foo> will be {"$doctype":" foo} (in compact form)
commentKey "comment" or "_comment" Name of the property key which will be used for the comment. For example, if commentKey: '$comment' then output of <!--note--> will be {"$comment":"note"} (in compact form)
parentKey "parent" or "_parent" Name of the property key which will be used for the parent. For example, if parentKey: '$parent' then output of <a></b></a> will be {"a":{"b":{$parent:_points_to_a}}} (in compact form)
typeKey "type" Name of the property key which will be used for the type. For example, if typeKey: '$type' then output of <a></a> will be {"elements":[{"$type":"element","name":"a"}]} (in non-compact form)
nameKey "name" Name of the property key which will be used for the name. For example, if nameKey: '$name' then output of <a></a> will be {"elements":[{"type":"element","$name":"a"}]} (in non-compact form)
elementsKey "elements" Name of the property key which will be used for the elements. For example, if elementsKey: '$elements' then output of <a></a> will be {"$elements":[{"type":"element","name":"a"}]} (in non-compact form)

Two default values mean the first is used for non-compact output and the second is for compact output.

TIP: In compact mode, you can further reduce output result by using fewer characters for key names {textKey: '_', attributesKey: '$', commentKey: 'value'}. This is also applicable to non-compact mode.

TIP: In non-compact mode, you probably want to set {textKey: 'value', cdataKey: 'value', commentKey: 'value'} to make it more consistent and easier for your client code to go through the contents of text, cdata, and comment.

Options for Custom Processing Functions

For XML → JS object / JSON, following custom callback functions can be supplied:

var convert = require('xml-js');
var xml = '<foo:Name>Ali</Name> <bar:Age>30</bar:Age>';
var options = {compact: true, elementNameFn: function(val) {return val.replace('foo:','').toUpperCase();}};
var result = convert.xml2json(xml, options);
console.log(result); // {"NAME":{"_text":"Ali"},"BAR:AGE":{"_text":"30"}}
Option Signature Description
doctypeFn (value, parentElement) To perform additional processing for DOCTYPE. For example, {doctypeFn: function(val) {return val.toUpperCase();}}
instructionFn (instructionValue, instructionName, parentElement) To perform additional processing for content of Processing Instruction value. For example, {instructionFn: function(val) {return val.toUpperCase();}}. Note: instructionValue will be an object if instructionHasAttributes is enabled.
cdataFn (value, parentElement) To perform additional processing for CData. For example, {cdataFn: function(val) {return val.toUpperCase();}}.
commentFn (value, parentElement) To perform additional processing for comments. For example, {commentFn: function(val) {return val.toUpperCase();}}.
textFn (value, parentElement) To perform additional processing for texts inside elements. For example, {textFn: function(val) {return val.toUpperCase();}}.
instructionNameFn (instructionName, instructionValue, parentElement) To perform additional processing for Processing Instruction name. For example, {instructionNameFn: function(val) {return val.toUpperCase();}}. Note: instructionValue will be an object if instructionHasAttributes is enabled.
elementNameFn (value, parentElement) To perform additional processing for element name. For example, {elementNameFn: function(val) {return val.toUpperCase();}}.
attributeNameFn (attributeName, attributeValue, parentElement) To perform additional processing for attribute name. For example, {attributeNameFn: function(val) {return val.toUpperCase();}}.
attributeValueFn (attributeValue, attributeName, parentElement) To perform additional processing for attributeValue. For example, {attributeValueFn: function(val) {return val.toUpperCase();}}.
attributesFn (value, parentElement) To perform additional processing for attributes object. For example, {attributesFn: function(val) {return val.toUpperCase();}}.

For JS object / JSON → XML, following custom callback functions can be supplied:

var convert = require('xml-js');
var json = '{"name":{"_text":"Ali"},"age":{"_text":"30"}}';
var options = {compact: true, textFn: function(val, elementName) {return elementName === 'age'? val + '';}};
var result = convert.json2xml(json, options);
console.log(result); // <foo:Name>Ali</Name> <bar:Age>30</bar:Age>
Option Signature Description
doctypeFn (value, currentElementName, currentElementObj) To perform additional processing for DOCTYPE. For example, {doctypeFn: function(val) {return val.toUpperCase();}.
instructionFn (instructionValue, instructionName, currentElementName, currentElementObj) To perform additional processing for content of Processing Instruction value. For example, {instructionFn: function(val) {return val.toUpperCase();}}. Note: instructionValue will be an object if instructionHasAttributes is enabled.
cdataFn (value, currentElementName, currentElementObj) To perform additional processing for CData. For example, {cdataFn: function(val) {return val.toUpperCase();}}.
commentFn (value, currentElementName, currentElementObj) To perform additional processing for comments. For example, {commentFn: function(val) {return val.toUpperCase();}}.
textFn (value, currentElementName, currentElementObj) To perform additional processing for texts inside elements. For example, {textFn: function(val) {return val.toUpperCase();}}.
instructionNameFn (instructionName, instructionValue, currentElementName, currentElementObj) To perform additional processing for Processing Instruction name. For example, {instructionNameFn: function(val) {return val.toUpperCase();}}. Note: instructionValue will be an object if instructionHasAttributes is enabled.
elementNameFn (value, currentElementName, currentElementObj) To perform additional processing for element name. For example, {elementNameFn: function(val) {return val.toUpperCase();}}.
attributeNameFn (attributeName, attributeValue, currentElementName, currentElementObj) To perform additional processing for attribute name. For example, {attributeNameFn: function(val) {return val.toUpperCase();}}.
attributeValueFn (attributeValue, attributeName, currentElementName, currentElementObj) To perform additional processing for attributeValue. For example, {attributeValueFn: function(val) {return val.toUpperCase();}}.
attributesFn (value, currentElementName, currentElementObj) To perform additional processing for attributes object. For example, {attributesFn: function(val) {return val.toUpperCase();}}.
fullTagEmptyElementFn (currentElementName, currentElementObj) Whether to generate full tag or just self closing tag for elements that has no sub elements. For example, {fullTagEmptyElementFn: function(val) {return val === 'foo'}}.

Command Line

Because any good library should support command line usage, this library is no different.

As Globally Accessible Command

npm install -g xml-js                       // install this library globally
xml-js test.json --spaces 4                 // xml result will be printed on screen
xml-js test.json --spaces 4 --out test.xml  // xml result will be saved to test.xml
xml-js test.xml --spaces 4                  // json result will be printed on screen
xml-js test.xml --spaces 4 --out test.json  // json result will be saved to test.json

As Locally Accessible Command

If you want to use it as script in package.json (can also be helpful in task automation via npm scripts)

npm install --save xml-js   // no need to install this library globally

In package.json, write a script:

...
  "dependencies": {
    "xml-js": "latest"
  },
  "scripts": {
    "convert": "xml-js test.json --spaces 4"
  }

Now in the command line, you can run this script by typing:

npm run convert             // task 'scripts.convert' will be executed

CLI Arguments

Usage: xml-js src [options]

  src                  Input file that need to be converted.
                       Conversion type xml->json or json->xml will be inferred from file extension.

Options:
  --help, -h           Display this help content.
  --version, -v        Display version number of this module.
  --out                Output file where result should be written.
  --spaces             Specifies amount of space indentation in the output.
  --full-tag           XML elements will always be in <a></a> form.
  --no-decl            Declaration directive <?xml?> will be ignored.
  --no-inst            Processing instruction <?...?> will be ignored.
  --no-attr            Attributes of elements will be ignored.
  --no-text            Texts of elements will be ignored.
  --no-cdata           CData of elements will be ignored.
  --no-doctype         DOCTYPE of elements will be ignored.
  --no-comment         Comments of elements will be ignored.
  --trim               Any whitespaces surrounding texts will be trimmed.
  --compact            JSON is in compact form.
  --native-type        Numbers and boolean will be converted (coerced) to native type instead of text.
  --always-array       Every element will always be an array type (applicable if --compact is set).
  --always-children    Every element will always contain sub-elements (applicable if --compact is not set).
  --text-key           To change the default 'text' key.
  --cdata-key          To change the default 'cdata' key.
  --doctype-key        To change the default 'doctype' key.
  --comment-key        To change the default 'comment' key.
  --attributes-key     To change the default 'attributes' key.
  --declaration-key    To change the default 'declaration' key.
  --instruction-key    To change the default 'processing instruction' key.
  --type-key           To change the default 'type' key (applicable if --compact is not set).
  --name-key           To change the default 'name' key (applicable if --compact is not set).
  --elements-key       To change the default 'elements' key (applicable if --compact is not set).

Contribution

Testing

To perform tests on this project, download the full repository from GitHub (not from npm) and then do the following:

cd xml-js
npm install
npm test

For live testing, use npm start instead of npm test.

Reporting

Use this link to report an issue or bug. Please include a sample code where the code is failing.

License

MIT

xml-js's People

Contributors

aircrisp avatar arlen22 avatar deniscarriere avatar hagedihag avatar maxim-mazurok avatar mk-pmb avatar mrhyde avatar nashwaan avatar princed avatar redrelay avatar servel333 avatar silentgert avatar simonmeusel avatar sjakos avatar st-sloth avatar techborn avatar winniehell avatar yarnsphere avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

xml-js's Issues

Convert XML->JSON->XML

Hey,

I apologize if this is posted in the wrong place, but looking at the Quick Start example we have

var convert = require('xml-js');
            var xml =
                '<?xml version="1.0" encoding="utf-8"?>' +
                '<note importance="high" logged="true">' +
                '    <title>Happy</title>' +
                '    <todo>Work</todo>' +
                '    <todo>Play</todo>' +
                '</note>';
            var op = { compact: true, spaces: 4 };
            var result1 = convert.xml2json(xml, op);
            console.log(result1);

Converting back,


        console.log(convert. json2xml(result1, op));

Which gives the following error:

json2xml.js:4 Uncaught ReferenceError: Buffer is not defined

Other than what's up there, I grabbed xml-js through

npm install --save xml-js

Thoughts?

Allow mapped value to be `undefined` in type for `_attributes`

I am extending ElementCompact and want to add some explicit optional properties:

export interface MyElement extends ElementCompact {
  _attributes: {
    id: string
    doIEvenExist?: 'yes' | 'no'
  }
}

Unfortunately with this I get TS error:

Property 'doIEvenExist' is incompatible with index signature.
  Type '"yes" | "no" | undefined' is not assignable to type 'string | number'.
    Type 'undefined' is not assignable to type 'string | number'.

I currently use the following workaround:

...
_attributes: ElementCompact['_attributes'] & {
...

Unexpected newline characters are inserted before sibling node elements

I don't know if this is easy to implement, but I am trying to do something like 'xml' -> json -> 'xml_1', and I want 'xml' and 'xml_1' to be exactly the same if I don't modify the intermediate 'json'.

Here is the problem. This is the original xml:

<source>
    <x id="START_TAG_SPAN" ctype="x-span"/>Support and resistance<x id="CLOSE_TAG_SPAN" ctype="x-span"/> levels represent prices where there &amp;
    has been exhaustive supply or demand, which can influence the price if it approaches this level again. Support
    acts like a floor: a price where investors tend to see good value, and heavy buying action keeps the price propped
    up above this level. Some traders buy around support if they think it will hold. Others place stops just below
    support, since a penetration suggests bearishness. Conversely, resistance acts like a ceiling: a price where
    investors tend to take profits and the selling action holds the price below this level. Some investors take profits
    near resistance if they think it will hold. Others place stops just above resistance, since a penetration suggests
    bullishness.
</source>

After being converted to JavaScript Object and then back, the xml becomes:

<source>
    <x id="START_TAG_SPAN" ctype="x-span"/>Support and resistance
    <x id="CLOSE_TAG_SPAN" ctype="x-span"/> levels represent prices where there &amp;
    has been exhaustive supply or demand, which can influence the price if it approaches this level again. Support
    acts like a floor: a price where investors tend to see good value, and heavy buying action keeps the price propped
    up above this level. Some traders buy around support if they think it will hold. Others place stops just below
    support, since a penetration suggests bearishness. Conversely, resistance acts like a ceiling: a price where
    investors tend to take profits and the selling action holds the price below this level. Some investors take profits
    near resistance if they think it will hold. Others place stops just above resistance, since a penetration suggests
    bullishness.

</source>

You could see there is a newline before the SECOND <x> node element. And I have no idea why there is another newline before </source>

Declaration parsing is wrong.

If you use .xml2js (not .xml2json!) function with an XML file that contain following declaration:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
you get wrong declaration attributes (there will be extra attribute with name 'undefined' and value undefined).
In my debuging I found that the following code in lib/xml2js.js is producing that wrong result.
The reason is wrong RegExp used to parse next attribute name and value:
/([\w:-]+)\s*=\s*"([^"]*)"|'([^']*)'|(\w+)\s*/
The | (boolean OR) has priority so that regexp actually accepts (key=(spaces)"value") OR ('value') OR (value(spaces)).
The working regexp is, for example:
/([\w:-]+)\s*=\s*(?:"([^"]*)"|'([^']*)'|(\w+))\s*/
Code with wrong regexp (lib\xml2js.js):
if (instruction.body && (instruction.name.toLowerCase() === 'xml' || options.instructionHasAttributes)) { while (instruction.body) { var attribute = instruction.body.match(/([\w:-]+)\s*=\s*"([^"]*)"|'([^']*)'|(\w+)\s*/); if (!attribute) { break; } attributes[attribute[1]] = attribute[2]; instruction.body = instruction.body.slice(attribute[0].length); // advance the string } }

json2xml sanitizes even when sanitize is set to false.

Also, xml2json crashes if it encounters an unescaped <, >, or & in the XML, but I am not sure whether you would consider that a bug.

Input:

<?xml version="1.0" encoding="UTF-8"?>
<note>
  <to>xml-js</to>
  <from>ACraig</from>
  <heading>Min Example</heading>
  <body>Here are some characters that get sanitized: " '</body>
</note>

Code:

var fs = require('fs');
var converter = require('xml-js');

fs.readFile('minexample.xml', 'utf8', function(err, xml) {
  if (err) throw err;
  console.log('XML:');
  console.log(xml);
  console.log('compact JSON:');
  var json1 = converter.xml2json(xml, {compact: true, spaces: 2});
  console.log(json1);
  console.log('back-converted XML from compact:');
  xmlback1 = converter.json2xml(json1, {compact: true, spaces: 2, sanitize: false});
  console.log(xmlback1);
  console.log('matches original:');
  console.log(xmlback1 == xml);
  fs.writeFile('backfromcompact.xml', xmlback1, function(err) {
    if(err) {
      return console.log(err);
    }
    console.log('Saved backfromcompact.xml.');
  });
  console.log('verbose JSON:');
  var json2 = converter.xml2json(xml, {compact: false, spaces: 2});
  console.log(json2);
  console.log('back-converted XML from verbose:');
  xmlback2 = converter.json2xml(json2, {compact: false, spaces: 2, sanitize: false});
  console.log(xmlback2);
  console.log('matches original:');
  console.log(xmlback2 == xml);
  fs.writeFile('backfromverbose.xml', xmlback2, function(err) {
    if(err) {
      return console.log(err);
    }
    console.log('Saved backfromverbose.xml.');
  });
});

Output:

XML:

<?xml version="1.0" encoding="UTF-8"?>
<note>
  <to>xml-js</to>
  <from>ACraig</from>
  <heading>Min Example</heading>
  <body>Here are some characters that get sanitized: " '</body>
</note>

compact JSON:

{
  "_declaration": {
    "_attributes": {
      "version": "1.0",
      "encoding": "UTF-8"
    }
  },
  "note": {
    "to": {
      "_text": "xml-js"
    },
    "from": {
      "_text": "ACraig"
    },
    "heading": {
      "_text": "Min Example"
    },
    "body": {
      "_text": "Here are some characters that get sanitized: \" '"
    }
  }
}

back-converted XML from compact:

<?xml version="1.0" encoding="UTF-8"?>
<note>
  <to>xml-js</to>
  <from>ACraig</from>
  <heading>Min Example</heading>
  <body>Here are some characters that get sanitized: &quot; &#39;</body>
</note>

matches original:
false
verbose JSON:

{
  "declaration": {
    "attributes": {
      "version": "1.0",
      "encoding": "UTF-8"
    }
  },
  "elements": [
    {
      "type": "element",
      "name": "note",
      "elements": [
        {
          "type": "element",
          "name": "to",
          "elements": [
            {
              "type": "text",
              "text": "xml-js"
            }
          ]
        },
        {
          "type": "element",
          "name": "from",
          "elements": [
            {
              "type": "text",
              "text": "ACraig"
            }
          ]
        },
        {
          "type": "element",
          "name": "heading",
          "elements": [
            {
              "type": "text",
              "text": "Min Example"
            }
          ]
        },
        {
          "type": "element",
          "name": "body",
          "elements": [
            {
              "type": "text",
              "text": "Here are some characters that get sanitized: \" '"
            }
          ]
        }
      ]
    }
  ]
}

back-converted XML from verbose:

<?xml version="1.0" encoding="UTF-8"?>
<note>
  <to>xml-js</to>
  <from>ACraig</from>
  <heading>Min Example</heading>
  <body>Here are some characters that get sanitized: &quot; &#39;</body>
</note>

matches original:
false
Saved backfromcompact.xml.
Saved backfromverbose.xml.

Special characters in XML attributes not being escaped correctly

Thanks very much for this very useful library. I've run into an issue since ampersands in XML attributes aren't being escaped by js2xml. This seems to have happened after the change described here: #26 (comment) however the StackOverflow answer that is referenced seems to be incorrect or just very badly structured? The spec extract that is quoted seems to contradict its recommendation and attributes with ampersands don't validate properly - e.g. try https://validator.w3.org/check with

<?xml version="1.0"?>
<elt attr="abc&def"></elt>

Processing Instructions are ignored

I have an XML document that includes a processing instruction. But when I run it through xml2js, the processing instruction gets left behind.

<?xml version="1.0"?>
<?myapi version="13.0"?>

Unexpected token when parsing XML to JSON

Hi, I'm trying to get a Json from an XML file.

It works with the given XML code:

var xml =
'' +
'' +
' <title>Happy</title>' +
' Work' +
' Play' +
'';

But it doesn't with my local xml file

var xml = require('./pregunta.xml')

This is the content:

Moodle QUIZ XML export ­Si un coche circula a 25m/s... ­Lo hace a 90km/h true false ­Si un coche circula a 25m/s... ­Lo hace a 50m/min true false ­Si un coche circula a 25m/s... ­Lo hace a 80km/h true false

I get the following error:

Error: Module parse failed: Unexpected token (1:0)
You may need an appropriate loader to handle this file type.
|
|
|

Could you please help me?
Thanks.

Array conversion doesn't work in different context

Hi,
In my application I run user scripts in a Node VM. I added your library to provide easy XML parsing and generation capabilities. Now I run into a problem when I try to convert an Array to XML. The array is created within the user script which runs in a sandbox context. The library is added to this context, but uses the wrapping context itself.

As far as I can tell, the problem are the instanceof Array checks in your conversion code. Because I have now two different versions of the Array object (one in each context) the check doesn't work anymore. See also https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof

Reusing the same context is currently not possible for me as the scripts have to run in a sandbox with limited access.

I created a test case that illustrates the problem:

var convert = require("../lib");
var Script = require("vm").Script;

describe("case by silentgert", function() {
      var context = {
        convert: convert,
        output: undefined,
      };
      var scriptCode = 
      "(function() {\n" +
      "  const obj = {\n" +
      "    customers : {\n" +
      "      customer: [\n" +
      "        {\n" +
      "          _text: 'John Doe',\n" +
      "          _attributes: {\n" +
      "            status: 'silver'\n" +
      "          }\n" +
      "        },\n" +
      "        {\n" +
      "          _text: 'Alice Allgood',\n" +
      "          _attributes: {\n" +
      "            status: 'gold'\n" +
      "          }\n" +
      "        }\n" +
      "      ]\n" +
      "    }\n" +
      "  };\n" +
      "  output = convert.js2xml(obj, { compact: true });\n" +
      "})()\n";

      var executableScript = new Script(scriptCode, {
        displayErrors: true,
      });
  
      it ('should convert Arrays in a different context', function() {
        executableScript.runInNewContext(context);
        expect(context.output).toEqual('<customers><customer status="silver">John Doe</customer><customer status="gold">Alice Allgood</customer></customers>');
      });
    });

Expect outcome:

<customers>
  <customer status="silver">John Doe</customer>
  <customer status="gold">Alice Allgood</customer>
</customers>

Observed outcome:

<customers>
  <customer>
    <0 status="silver">John Doe</0>
    <1 status="gold">Alice Allgood</1>
  </customer>
</customers>

Possible solution:
In the Mozilla docs they mention to use Array.isArray for secure checks. Another option might be to look at how Lodash is doing the isArray check (without adding the dependency).

If you are okay with it, I can look into the issue and provide a pull request.

Whitespace between elements is lost

Hi, thanks for your great library!

xml-js version is v1.4.1

It seems that whitespace between two sibling elements is lost when converted if it is the only content. The following code illustrates the problem:

const convert = require('xml-js');
const xml = '<outer> This is <inner> some</inner> <inner>Text </inner> </outer>';
const json = convert.xml2json(xml, {compact: false, alwaysChildren: false});
const result = convert.json2xml(json, {compact: false});
console.log(result);
// <outer> This is <inner> some</inner><inner>Text </inner></outer>

As you can see, " This is " still has the whitespace after conversion, as well as " some " and " Text ", but not the whitespace between the first closing and second opening "inner" tags.

Is there some setting that I missed or is this a problem with the library?

Kind regards,
Daniel

Add Typescript 2.0 definition to DefinitelyTyped

I haven't tried all of the XML libraries, but this is by far the best one I've seen.

I'd like to add a Typescript definition to this project, it will take me a while before understanding your top level schema, but so far it's very clean!

Adding JSDocs to the functions really help out as well.

There's two approaches:

  1. Convert project into Typescript (it's a lot of work)
  2. Add definitions to http://definitelytyped.org/ (Just need to add the schema of the project, *less work)

Once I get something up and running, I'd like for you @nashwaan to review it, Typescript definitions are pretty straightforward.

I've written a few before, have a look at these examples:

https://gist.github.com/DenisCarriere/11415dc23d5cab134180e86c07f06ae3

Enhacement: Add a new option to not show the "_text" option at all

We can introduce an option to directly show the corresponding XML value instead of showing it under _text.

Eg.
XML:

<key1>value1</key1>
<key2>value2</key2>

COMPACT JS-OBJECT:

{
    key1: {_text: "value1"},
    key2: {_text: "value2"}
}

With the new option:

{
    key1: "value1",
    key2: "value2"
}

For my own usage, I used this recursive workaround for the same, hence thought for the enhancement : https://github.com/noveens/js-owncloud-client/pull/19/files#diff-b9b823e719730e39048dffbb512e743cR777

Thanks.

XML declaration with attributes using single quotes

When using xml2js, this XML declaration is detected:

<?xml version="1.0" encoding="UTF-8"?>

Whilst this one, which I believe is legal, is not detected:

<?xml version='1.0' encoding='UTF-8'?>

Thank you for your good work!

Is there an option to parent the fields of an `_attributes object` on the parent object.

Pardon me if that question is very poorly worded.

Basically I have an XML document where each XML element has attributes, but no child elements. So it's safe to skip the creation of an _attributes object. The attributes themselves can go right on the generated JSON object.

Example: Given

<a val="5"/>
<a val="6"/>

I'd like the output to be (Note, I can make a plural myself as in this example)

{
  "as": [
    {
      "val": "5"
    },
    {
      "val": "6"
    }
  ]
}

rather than

{
  "a": [
    {
      "_attributes": {
        "val": "5"
      }
    },
    {
      "_attributes": {
        "val": "6"
      }
    }
  ]
}

returns "true", "false" as string instead of boolean

Since true and false are expected to be boolean values, negating and/or testing explicitly failes.

XML
<a show="false"/>

to JSON

{
  "a": {
    "_attributes": {
      "show": "false"
    }
  }
}

Testing

if (!a._attributes.show) // this is will not end well

Angular 2+

How can I use it with angular 2+???????

Can't convert from JSON to XML

Hello Please let me know of any changes in the configurations or options of the module.

It is not able to convert back the file of JSON to XML.

Extra spaces with text elements when `spaces=number`

I'm getting extra white spaces when I'm using the text elements when going two elements deep with compact=false.

const Elements = {
  type: 'element',
  name: 'a',
  elements: [{
    type: 'element',
    name: 'b',
    elements: [{type: 'text', text: 'foo bar'}],
  }]
}
const xml = convert.js2xml({ elements: [Elements] }, { compact: false, spaces: 4 })
console.log(xml)

xml output (notice 4 extra spaces after foo bar)

<a>
    <b>foo bar    </b>
</a>

Strange because when using compact=true those spaces don't appear.

const Elements = {
  a: {
    b: {
      _text: 'foo bar',
    }
  }
}
const xml = convert.js2xml(Elements, { compact: true, spaces: 4 })
console.log(xml)

xml output

<a>
    <b>foo bar</b>
</a>

How insert parent tag for convert from json to xml?

Hello!
I have a array objects.

[ { type: 'element', name: 'material', elements: [ [Object] ] },
  { type: 'element', name: 'material', elements: [ [Object] ] },
  { type: 'element', name: 'material', elements: [ [Object] ] },
  { type: 'element', name: 'material', elements: [ [Object] ] },
  { type: 'element', name: 'material', elements: [ [Object] ] } ]

And after convert I getting the next xml

<material><mattext><p><font size="14"><strong>....</material>
<material><mattext><p><font size="14"><strong>....</material>

But if execute xml2json for result above then, I receive the error.
Error: Text data outside of root node.
Line: 248
Column: 26
Could you tell me how I can insert root node ?

DOCTYPE declaration cannot be parsed

I want to convert standard MLP (Mobile Positioning Protocol) xml to JSON and back.
Here is the first 3 line:

%extension;]>

<svc_init ver="3.0.0">

After it is converted to JSON:
{
"_declaration": {
"_attributes": {
"version": "1.0",
"encoding": "UTF-8"
}
},
"svc_init": {
"_attributes": {
"ver": "3.0.0"
}

convert.xml2json(xml, { compact: true, spaces: 6, ignoreCdata: false });
I lost !DOCTYPE.

Can you add ignoreDoctype to the code?

xml to js is not converting empty comments

sample xml:

   <test><!----></test>

output:

{
  "elements": [
    {
      "type": "element",
      "name": "test-child"
    }
  ]
}

expected output:

{
  "elements": [
    {
      "type": "element",
      "name": "test-child",
      "elements": [
        {
          "type": "comment",
          "comment": ""
        }
      ]
    }
  ]
}

Is there an option to do this?

npm file size pretty big

Just noticed that your published npm bundle is pretty large, almost 1MB (824KB).

$ du -h node_modules/xml-js
184K    xml-js/artwork
 12K    xml-js/bin
 32K    xml-js/lib
156K    xml-js/test/browse-jasmine/lib/jasmine-2.5.2
156K    xml-js/test/browse-jasmine/lib
452K    xml-js/test/browse-jasmine
520K    xml-js/test
824K    xml-js
$ ls -lh
-rw-r--r--   1 mac  staff   1.1K May  2  2016 LICENSE
-rw-r--r--   1 mac  staff    17K Nov  4 00:15 README.md
-rw-r--r--   1 mac  staff   456B Jun  7  2016 appveyor.yml
drwxr-xr-x   5 mac  staff   170B Jan 24 10:07 artwork
drwxr-xr-x   4 mac  staff   136B Jan 24 10:07 bin
-rw-r--r--   1 mac  staff    34B Sep  7 03:42 index.js
drwxr-xr-x   8 mac  staff   272B Jan 24 10:07 lib
-rw-r--r--   1 mac  staff   5.2K Jan 24 10:07 package.json
drwxr-xr-x  13 mac  staff   442B Jan 24 10:07 test

Your core javascript files (index.js + lib/) is only 32KB.

Inside your test cases you've got 520KB that you ship with your npm bundle also artwork is 180KB, might be worth adding an .npmignore file to your repo and exclude some of the files that are not needed.

**Proposed .npmignore

artwork/
test/

Unable to load xml-js with SystemJS

Hello, this is most likely not an issue but a request for support. However, I don't see other ways to request support in this case. I want to use the npm package xml-js in Angular 2 and use SystemJS as the package loader. I haven't been able to get this working. I can experiment with xml-js in a node-script. That works fine. But the "require" statement that works so good in node scripts has to be replaced by something else. Something in TypeScript with which to instruct SystemJS to load the package I suppose. Advice would be highly appreciated. Jan Verheul ([email protected]).

Error: Text data outside of root node

XML - >
<?xml version="1.0" encoding="utf-8"?>
<dp:ListServicesReply ReturnCode="0" xmlns:dp="http://www.cisco.com/vtg/diagnosticportal">
<dp:Schema Version="1.0" />
<dp:ServiceList>
<dp:Service Name="Cisco ICM usgd1 LoggerA" Description="Provides Call Logging services for Instance usgd1" Status="Running" StartupType="Auto" LogOnAs="****" />
<dp:Service Name="Cisco ICM Diagnostic Framework" Description="Provides a web-based diagnostic service for Cisco Unified ICM, Contact Center Enterprise application." Status="Running" StartupType="Auto" LogOnAs="LocalSystem" />
</dp:ServiceList>
</dp:ListServicesReply>

Getting this stack when attempting to parse the above XML doc

/node_modules/sax/lib/sax.js:984

  throw this.error
  ^

Error: Text data outside of root node.
Line: 0
Column: 15
Char: ]

at error (./node_modules/sax/lib/sax.js:667:10)
at strictFail (./node_modules/sax/lib/sax.js:693:7)
at Object.write (./node_modules/sax/lib/sax.js:1048:15)
at module.exports (./node_modules/xml-js/lib/xml2js.js:226:16)
at Object.module.exports as xml2json
at Object. (./portico.js:27:23)
at Module._compile (module.js:556:32)
at Object.Module._extensions..js (module.js:565:10)
at Module.load (module.js:473:32)
at tryModuleLoad (module.js:432:12)

Always prefix elements with namespace

Hi there, I have a use case where the XML is not fixed, it can be anything according to a certain spec.
At the moment I am looking for a specific property, let's take the following examples:

<d:multistatus xmlns="DAV:">
	<response>
		<href>/</href>
		    <propstat>
			    <prop>
					<current-user-principal>
						<href>/principals/users/johndoe/</href>
					</current-user-principal>
			    </prop>
			<status>HTTP/1.1 200 OK</status>
		</propstat>
	</response>
</d:multistatus>
<d:multistatus xmlns:d="DAV:">
    <d:response>
        <d:href>/</d:href>
        <d:propstat>
            <d:prop>
                <d:current-user-principal>
                    <d:href>/principals/users/johndoe/</d:href>
                </d:current-user-principal>
            </d:prop>
            <d:status>HTTP/1.1 200 OK</d:status>
        </d:propstat>
    </d:response>
</d:multistatus>

You see one time I get the XML with a global namespace defined to "DAV:" making all elements part of that namespace and the other time theres a namespace of "d" that maps the "d:" to "DAV:".

When I check the JSON object for existence of "d:multistatus", it will return false on the first part of xml while there actually is a "multistatus" element. What I would like to see is that every element in the XML is prefixed by the namespace. So that in both above cases, the elements are called "DAV:multistatus" instead of "d:multistatus" or just "multistatus".

I hope you know what I mean, and can help me with this problem. Maybe there is already a way to get the elements correctly respecting the namespaces.. I'd like to hear from you :)

Add optional `standalone` in the type for _declaration` attributes

When supplying a value for standalone in _declaration attributes I get TS error:

Object literal may only specify known properties, and 'standalone' does not exist in type '{ version?: string | number | undefined; encoding?: string | number | undefined; } | undefined'.

Not sure if there are any other valid props.

Option to indent attributes

I would like the option of getting the following output, with each attribute on a separate line with indentation

<parent
  bar=1
  baz="hello"
>
  <child
    attr1="a"
    attr2="b"
  />
</parent>

Declaration parsing fails with single quotes

I'm running version 0.9.7. This works:

> var convert = require('xml-js');
> convert.xml2js('<?xml version="1.0" encoding="utf-8"?>')
{ declaration: { attributes: { version: '1.0', encoding: 'utf-8' } } }

But the same fails with single quotes for the attributes:

> convert.xml2js("<?xml version='1.0' encoding='utf-8'?>")
{ declaration: { attributes: { undefined: undefined } } }

I haven't found a formal reference, but from forum posts it sounds like both single or double quotes should be valid for declarations.

Thanks!

remove _text

"title": {
"_text": "You Won't Believe What These 31 Oscar Winners Look Like Now"
},

Request feature: Strip XMLNS data

I have XML files that are exported from Excel and have XMLNS prefix data. I don't need to use the data and it's awkward to use objects created with colons in the key.

The fix that meets my needs is simply to add this line at line 140 of xml2js.js:
name = name.slice ( name.search ( ':' ) + 1 ) ;

I suspect a full implementation would be more work and I'm happy to do that and propose a check-in if you'd like.

conversion to xml is not working at all

var convert = require('xml-js');

console.log(convert.json2xml(JSON.stringify({"name":'john'})));
console.log(convert.js2xml(JSON.stringify({"name":'john'})));
console.log(convert.json2xml({"name":'john'}));
console.log(convert.js2xml({"name":'john'}));

all are printing ""

Can't resolve 'stream'

WARNING in ./node_modules/xml-js/node_modules/sax/lib/sax.js
Module not found: Error: Can't resolve 'stream' in '/home/xxx/xxx/xxx/my-app/node_modules/xml-js/node_modules/sax/lib'

js2xml only returns empty string ""

I installed running version 1.4.2 and tried to run one of the examples from the npm site .

Here's the code that I've been trying to run w/o success. Only seem to work xml > js and not the other way around.

var options = {
spaces: 3,
compact: false,
fullTagEmptyElement: true,
ignoreDeclaration: false,
ignoreInstruction: false,
ignoreAttributes: false
};

var xml =
'' +
'' +
' <title>Happy</title>' +
' Work' +
' Play' +
'';
var someJs = {"a":{"_text":"Hi There"}};

// works as expected
var result1 = convert.xml2js(xml, {compact: true, spaces: 4});

// doesn't work - only returns empty string
var restult2 = convert.js2xml(someJs, options);

Minor: get rid of backslash double quotes

This is not a functional error but the published format can be improved:

The input XML string:

<?xml version="1.0" encoding="utf-8" ?><l key="license_number_1"> <s id="Pharma HK Central" ip="xxxxxx0" port="yyyyyy0"/> <s id="Pharma Paris" ip="xxxxxx1" port="yyyyyy1"/> <s id="Ph Bogota" ip="xxxxxx2" port="yyyyyy2"/></l>

The xml to JSON code:

let options = {
        compact: true,
        trim: true,
        ignoreDeclaration: true,
        ignoreComment: true,
        alwaysChildren: false,
        attributesKey: "$att",
        textKey: "$val"
    };
   let jsonObj = xml_js.xml2json(xmlString, options);
   console.log(JSON.stringify(jsonObj));

Conversion output:

"{"l":{"$att":{"key":"license_number_1"},"s":[{"$att":{"id":"Pharma HK Central","ip":"xxxxxx0","port":"yyyyyy0"}},{"$att":{"id":"Pharma Paris","ip":"xxxxxx1","port":"yyyyyy1"}},{"$att":{"id":"Ph Bogota","ip":"xxxxxx2","port":"yyyyyy2"}}]}}"

Expected:

{"l":{"$att":{"key":"license_number_1"},"s":[{"$att":{"id":"Pharma HK Central","ip":"xxxxxx0","port":"yyyyyy0"}},{"$att":{"id":"Pharma Paris","ip":"xxxxxx1","port":"yyyyyy1"}},{"$att":{"id":"Ph Bogota","ip":"xxxxxx2","port":"yyyyyy2"}}]}}

Of course, the backslah before double quotes make sense because of the output string's leading and trailing double quotes, and, again, it doesn't affect the correct use of the JSON object.
But it would be more readable to have the clean output, other alike packages, less functional than 'xml-js' do it.

Do not generate attributes with value `undefined`

I use some optional attributes that I want to render only when they are set.
When some of them are undefined, current xml output is myAttr="undefined", which makes the document not valid.
My current workaround is to use lodash with

{
  _arguments: pickBy({
	someProp: 'fixed',
	otherProp1: optionalValue1,
	...
	otherPropN: optionalValueN,
  })
}

Tidy XML output

I recently switched to this Node package for its versatility and I'm loving it. However, one thing I was missing is an option to format the XML output when using js2xml(). Consider the following example.

1.) Input object:

var obj = {
  _comment: " Released under The MIT License ",
  snippet: {
    content: {
      _cdata: "console.log($1)"
    },
    tabTrigger: {
      _text: "log"
    },
    scope: {
      _text: "source.js"
    }
  }
};

2.) Output XML – convert.json2xml(obj, {compact: true}):

<!-- Released under The MIT License --><snippet><content><![CDATA[console.log($1)]]></content><tabTrigger>log</tabTrigger><scope>source.js</scope></snippet>

3.) Output XML – convert.json2xml(obj, {compact: false, spaces: 4}):

<!-- The MIT License -->
    <snippet>
        <content/>
        <tabTrigger/>
        <scope/></snippet>

The output in 2.) is technically valid, but I would prefer an option that produces tidy/beautified XML. Unless I did something wrong, adding the spaces: 4 options produces an undesired result, why is both _cdata and _text missing?

Expected result (also note the indentation):

<!-- The MIT License -->
<snippet>
    <content><![CDATA[console.log($1)]]></content>
    <tabTrigger>log</tabTrigger>
    <scope>source.js</scope>
</snippet>

Not sure whether this is a bug or a misunderstanding of mine.

PS: I also tried converting the object to JSON with pretty-print and then use json2xml() instead – the results are the same!

CDATA is written with indentation and newlines

I read the following XML with xml2json:

<?xml version="1.0"?>
<group>
	<name><![CDATA[An example name]]></name>
</group>

but writing it back with json2xml gives me:

<?xml version="1.0"?>
<group>
    <name>
        <![CDATA[My modified name]]>
    </name>
</group>

This is is not the expected XML output.
The CDATA section is not a TAG and should not be handled as one and therefore not be indented.
Sure, its easier to read, but its not 100% correct.
It would be great to fix that or make a config parameter for that behaviour.

example code:

var xmljs = require('xml-js');
var xmlIn = '<?xml version="1.0"?>\n<group>\n\t<name><![CDATA[An example name]]></name>\n</group>';
console.log('xmlIn:\n',xmlIn);

var jsonIn = xmljs.xml2json(xmlIn, {
	compact: true
});
var obj = JSON.parse(jsonIn);

// code to modify obj
obj.group.name._cdata = 'My modified name';

var jsonOut = JSON.stringify(obj);

var xmlOut = xmljs.json2xml(jsonOut, {
	compact	: true, 
	spaces	: 4,
	fullTagEmptyElement: true
});

console.log('xmlOut:\n',xmlOut);

Solution
file: js2xml.js

  1. Function: writeElementsCompact, Line 143
                case options.cdataKey: xml += writeCdata(element, options); break;
  1. Function: hasContent, Line 102
                case options.cdataKey:
                	return false;

RangeError: Maximum call stack size exceeded

Hi,
I am trying to parse json to xml (both in code with json2xml and with cli), but it gives me this error:

/Users/.../node_modules/xml-js/lib/js2xml.js:35
    return (!firstLine && options.spaces ? '\n' : '') + Array(depth + 1).join(options.spaces);
                                                                         ^

RangeError: Maximum call stack size exceeded
    at Array.join (native)
    at writeIndentation (/Users/.../node_modules/xml-js/lib/js2xml.js:35:74)
    at writeElementsCompact (/Users/.../node_modules/xml-js/lib/js2xml.js:225:37)
    at writeElementCompact (/Users/.../node_modules/xml-js/lib/js2xml.js:204:12)
    at writeElementsCompact (/Users/.../node_modules/xml-js/lib/js2xml.js:225:83)
    at writeElementCompact (/Users/.../node_modules/xml-js/lib/js2xml.js:204:12)
    at writeElementsCompact (/Users/.../node_modules/xml-js/lib/js2xml.js:225:83)
    at writeElementCompact (/Users/.../node_modules/xml-js/lib/js2xml.js:204:12)
    at writeElementsCompact (/Users/.../node_modules/xml-js/lib/js2xml.js:225:83)
    at writeElementCompact (/Users/.../node_modules/xml-js/lib/js2xml.js:204:12)
    at writeElementsCompact (/Users/.../node_modules/xml-js/lib/js2xml.js:225:83)
    at writeElementCompact (/Users/.../node_modules/xml-js/lib/js2xml.js:204:12)
    at writeElementsCompact (/Users/.../node_modules/xml-js/lib/js2xml.js:225:83)
    at writeElementCompact (/Users/.../node_modules/xml-js/lib/js2xml.js:204:12)
    at writeElementsCompact (/Users/.../node_modules/xml-js/lib/js2xml.js:225:83)
    at writeElementCompact (/Users/.../node_modules/xml-js/lib/js2xml.js:204:12)

The original json file is:

{
  "vertical": {
    "-display_name": "Exercise",
    "html": { "-url_name": "12345" },
    "lti_consumer": {
      "-url_name": "12345",
      "-xblock-family": "xblock.v1",
      "-accept_grades_past_due": "false",
      "-weight": "14.0",
      "-has_score": "true",
      "-display_name": "Exercise",
      "-ask_to_send_username": "true",
      "-ask_to_send_email": "true",
      "-button_text": "Launch Exercise",
      "-custom_parameters": "none",
      "-lti_id": "id",
      "-launch_target": "new_window",
      "-launch_url": "url"
    }
  }
}

I would be grateful for any insights!

Thanks!
Yve

json2xml not working as intended.

To reproduce, I've first made an XML file that looks similar to what I'm looking for :

<ZohoCreator>
    <applicationslist>
        <application name="testapp">
            <formlist>
                <form name="Untitled_Form">
                    <add>
                        <field name="Subform_Single_Line">
                            <value>Value1</value>
                        </field>
                    </add>
                </form>
                <form name="Untitled_Form">
                    <add>
                        <field name="Subform_Single_Line">
                            <value>Value2</value>
                        </field>
                    </add>
                </form>
            </formlist>
        </application>
        <application name="derp">
            <formlist>
            </formlist>
        </application>
    </applicationslist>
</ZohoCreator>

Which gave me this :

{
    "ZohoCreator": {
        "applicationslist": {
            "application": [
                {
                    "_attributes": {
                        "name": "testapp"
                    },
                    "formlist": {
                        "form": [
                            {
                                "_attributes": {
                                    "name": "Untitled_Form"
                                },
                                "add": {
                                    "field": {
                                        "_attributes": {
                                            "name": "Subform_Single_Line"
                                        },
                                        "value": {
                                            "_text": "Value1"
                                        }
                                    }
                                }
                            },
                            {
                                "_attributes": {
                                    "name": "Untitled_Form"
                                },
                                "add": {
                                    "field": {
                                        "_attributes": {
                                            "name": "Subform_Single_Line"
                                        },
                                        "value": {
                                            "_text": "Value2"
                                        }
                                    }
                                }
                            }
                        ]
                    }
                },
                {
                    "_attributes": {
                        "name": "derp"
                    },
                    "formlist": {}
                }
            ]
        }
    }
}

So far so good. Now that I've got a well-formatted json according to your library's needs, let's try parsing it again, but backwards this time!

<ZohoCreator>
    <applicationslist>
        <application>
            <0 name="testapp">
                <formlist>
                    <form>
                        <0 name="Untitled_Form">
                            <add>
                                <field name="Subform_Single_Line">
                                    <value>Value1</value>
                                </field>
                            </add>
                        </0>
                        <1 name="Untitled_Form">
                            <add>
                                <field name="Subform_Single_Line">
                                    <value>Value2</value>
                                </field>
                            </add>
                        </1>
                    </form>
                </formlist>
            </0>
            <1 name="derp">
                <formlist/>
            </1>
        </application>
    </applicationslist>
</ZohoCreator>

Yeah. It's nothing like what we got. This is kinda killing it for me, as I was trying to convert a JSON into an XML in a easier fashion.

order gets changed while conversion.

Hi ,

I am trying to convert xml data to json and after some processing i want to convert back to xml. For testing purpose i am converting below data which is part of my xml file.

    <ProfAlign name="Layout">
	<PVI>0.77.906</PVI>
	<ParaCurve length="200.">247.13 91.807</ParaCurve>
	<PVI>1658.62622142 104.85033164</PVI>
   </ProfAlign>

xml2json ({compact: true, spaces: 4}) converts this data as below,

"ProfAlign": [
{
"_attributes": {
"name": "Layout"
},
"PVI": [
{
"_text": "0. 77.906"
},
{
"_text": "1658.62622142 104.85033164"
}
],
"ParaCurve": {
"_attributes": {
"length": "200."
},
"_text": "247.13 91.807"
}
}

And json2xml ({indentAttributes: false, spaces: '\t', fullTagEmptyElement: true, compact: true}) converts back to xml like below,

  <ProfAlign name="Layout">
	<PVI>0.77.906</PVI>
	<PVI>1658.62622142 104.85033164</PVI>
	<ParaCurve length="200.">247.13 91.807</ParaCurve>
  </ProfAlign>

As you can see, order is changed. Please suggest how to handle this.

Add return type for `xml2js`

Maybe I am missing something, but I believe xml2js should be marked as returning Element | ElementCompact and not just any.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.