Coder Social home page Coder Social logo

davidcalhoun / jstoxml Goto Github PK

View Code? Open in Web Editor NEW
172.0 7.0 23.0 834 KB

JavaScript object to XML converter (useful for RSS, podcasts, GPX, AMP, etc)

License: MIT License

JavaScript 99.81% Shell 0.19%
xml rss podcast amp html amp-html jsx jsx-syntax gpx

jstoxml's Introduction

jstoxml

npm downloads

Convert JavaScript objects (and JSON) to XML (for RSS, Podcasts, etc.)

Everyone loves JSON, and more and more folks want to move that direction, but we still need things outputted in XML! Particularly for RSS feeds and Podcasts.

This is inspired by node-jsontoxml, which was found to be a bit too rough around the edges. jstoxml attempts to fix that by being more flexible.

Installation

  • npm install jstoxml

Simple example

import { toXML } from 'jstoxml';

// toXML(content, config)
const content = {
    a: {
        foo: 'bar'
    }
};
const config = {
    indent: '    '
};

toXML(content, config);
/*
Output:
`<a>
    <foo>bar</foo>
</a>`
*/

Configuration object options (passed as second parameter to toXML())

Key name Type Default Description
indent string Indent string, repeated n times (where n=tree depth).
header string, boolean Outputs a simple XML 1.0 UTF-8 header when true. Can also be set to a custom string.
attributeReplacements object { "<": "&lt;", ">": "&gt;", "&": "&amp;", "\"": "&quot;" } XML attribute value substrings to replace (e.g. <a attributeKey="attributeValue" />). Does not double encode HTML entities (e.g. &lt; is preserved and NOT converted to &amp;lt).
attributeFilter function Filters out attributes based on user-supplied function.
attributeExplicitTrue boolean false When true explicitly outputs true attribute value strings, e.g. <a foo='true' /> instead of <a foo />.
contentMap function Custom map function to transform XML content. Runs after contentReplacements.
contentReplacements object { "<": "&lt;", ">": "&gt;", "&": "&amp;", "\"": "&quot;" } XML content strings to replace (e.g. <a>This & that</a> becomes <a>This &amp; that</a>).
selfCloseTags boolean true Whether tags should be self-closing.

Changelog

Version 3.2.0

  • new config option selfCloseTags added which is used as an easier global setting to enable/disable self-closing tags.

Version 3.1.0

  • config option contentMap can now be passed to transform any XML content. For instance, if you want <a>null</a> to instead appear as <a></a> you pass in contentMap: (content) => { return content === null ? '' : content }
  • fixed an issue with improper line breaks and indenting with null content

Version 3.0.0

  • BREAKING CHANGE: config option attributesFilter has been renamed attributeReplacements
  • BREAKING CHANGE: config option filter has been renamed contentReplacements
  • CDATA blocks are now untouched (no HTML entity replacements) and unindented (#56)
  • true attribute values can now be outputted by setting config option attributeExplicitTrue: true (#57)
  • attributes can now be filtered out by supplying a custom function to the new config option attributeFilter. For instance, to remove null attribute values from the output, you can supply the config option attributeFilter: (key, val) => val === null (#58 and #10)
  • devDependencies: migrated from babel-eslint to @babel/eslint-parser, migrated from uglify-es to uglify-js

Version 2.2.0

  • Initial support for XML comments (#47)

Version 2.1.1

  • Fix for #48 (various 0-depth issues, bad "is output start" logic)

Version 2.0.0 (breaking)

  • New: automatic entity escaping for &, <, and > characters. In addition, quotes " in attributes are also escaped (see #41). Prior to this, users had to provide their own filter manually. Note that jstoxml makes an effort not to escape entities that appear to have already been encoded, to prevent double-encoding issues.
    • E.g. toXML({ foo: '1 < 2 & 2 > 1' }); // -> "<foo>1 &lt; 2 &amp; 2 &gt; 1</foo>"
    • To restore the default behavior from v1.x.x, simply pass in false to filter and attributesFilter options: toXML({ foo: '1 < 2 & 2 > 1' }, { filter: false, attributesFilter: false }); // -> "<foo>1 < 2 & 2 > 1</foo>"

For more changelog history, see CHANGELOG.md.

Past changes

  • See CHANGELOG.md for a full history of changes.

Other Examples

First you'll want to require jstoxml in your script, and assign the result to the namespace variable you want to use (in this case jstoxml):

// Node
const { toXML } = require('jstoxml');

// Browser (with the help of something like Webpack or Rollup)
import { toXML } from 'jstoxml';

// Browser global fallback (requires no bundler)
var toXML = window.jstoxml.toXML;

Example 1: Simple object

toXML({
    foo: 'bar',
    foo2: 'bar2'
});

Output:

<foo>bar</foo><foo2>bar2</foo2>

Note: because JavaScript doesn't allow duplicate key names, only the last defined key will be outputted. If you need duplicate keys, please use an array instead (see Example 2 below).

Example 2: Simple array (needed for duplicate keys)

toXML([
    {
        foo: 'bar'
    },
    {
        foo: 'bar2'
    }
]);

Output:

<foo>bar</foo><foo>bar2</foo>

Example 3: Simple functions

toXML({ currentTime: () => new Date() });

Output:

<currentTime>Mon Oct 02 2017 09:34:54 GMT-0700 (PDT)</currentTime>

Example 4: XML tag attributes

toXML({
    _name: 'foo',
    _content: 'bar',
    _attrs: {
        a: 'b',
        c: 'd'
    }
});

Output:

<foo a="b" c="d">bar</foo>

Example 5: Tags mixed with text content

To output text content, set a key to null:

toXML({
    text1: null,
    foo: 'bar',
    text2: null
});

Output:

text1<foo>bar</foo>text2

Example 6: Nested tags (with indenting)

const xmlOptions = {
    header: false,
    indent: '  '
};

toXML(
    {
        a: {
            foo: 'bar',
            foo2: 'bar2'
        }
    },
    xmlOptions
);

Output:

<a>
  <foo>bar</foo>
  <foo2>bar2</foo2>
</a>

Example 7: Nested tags with attributes (with indenting)

const xmlOptions = {
    header: false,
    indent: '  '
};

toXML(
    {
        ooo: {
            _name: 'foo',
            _attrs: {
                a: 'b'
            },
            _content: {
                _name: 'bar',
                _attrs: {
                    c: 'd'
                }
            }
        }
    },
    xmlOptions
);

Output:

<ooo>
  <foo a="b">
    <bar c="d"/>
  </foo>
</ooo>

Note that cases like this might be especially hard to read because of the deep nesting, so it's recommend you use something like this pattern instead, which breaks it up into more readable pieces:

const bar = {
    _name: 'bar',
    _attrs: {
        c: 'd'
    }
};

const foo = {
    _name: 'foo',
    _attrs: {
        a: 'b'
    },
    _content: bar
};

const xmlOptions = {
    header: false,
    indent: '  '
};

return toXML(
    {
        ooo: foo
    },
    xmlOptions
);

Example 8: Complex functions

Function outputs will be processed (fed back into toXML), meaning that you can output objects that will in turn be converted to XML.

toXML({
    someNestedXML: () => {
        return {
            foo: 'bar'
        };
    }
});

Output:

<someNestedXML><foo>bar</foo></someNestedXML>

Example 9: RSS Feed

const xmlOptions = {
    header: true,
    indent: '  '
};

toXML(
    {
        _name: 'rss',
        _attrs: {
            version: '2.0'
        },
        _content: {
            channel: [
                {
                    title: 'RSS Example'
                },
                {
                    description: 'Description'
                },
                {
                    link: 'google.com'
                },
                {
                    lastBuildDate: () => new Date()
                },
                {
                    pubDate: () => new Date()
                },
                {
                    language: 'en'
                },
                {
                    item: {
                        title: 'Item title',
                        link: 'Item link',
                        description: 'Item Description',
                        pubDate: () => new Date()
                    }
                },
                {
                    item: {
                        title: 'Item2 title',
                        link: 'Item2 link',
                        description: 'Item2 Description',
                        pubDate: () => new Date()
                    }
                }
            ]
        }
    },
    xmlOptions
);

Output:

<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>RSS Example</title>
    <description>Description</description>
    <link>google.com</link>
    <lastBuildDate>Sat Jul 30 2011 18:14:25 GMT+0900 (JST)</lastBuildDate>
    <pubDate>Sat Jul 30 2011 18:14:25 GMT+0900 (JST)</pubDate>
    <language>en</language>
    <item>
      <title>Item title</title>
      <link>Item link</link>
      <description>Item Description</description>
      <pubDate>Sat Jul 30 2011 18:33:47 GMT+0900 (JST)</pubDate>
    </item>
    <item>
      <title>Item2 title</title>
      <link>Item2 link</link>
      <description>Item2 Description</description>
      <pubDate>Sat Jul 30 2011 18:33:47 GMT+0900 (JST)</pubDate>
    </item>
  </channel>
</rss>

Example 10: Podcast RSS Feed

(see the Apple docs for more information)

const xmlOptions = {
    header: true,
    indent: '  '
};

toXML(
    {
        _name: 'rss',
        _attrs: {
            'xmlns:itunes': 'http://www.itunes.com/dtds/podcast-1.0.dtd',
            version: '2.0'
        },
        _content: {
            channel: [
                {
                    title: 'Title'
                },
                {
                    link: 'google.com'
                },
                {
                    language: 'en-us'
                },
                {
                    copyright: 'Copyright 2011'
                },
                {
                    'itunes:subtitle': 'Subtitle'
                },
                {
                    'itunes:author': 'Author'
                },
                {
                    'itunes:summary': 'Summary'
                },
                {
                    description: 'Description'
                },
                {
                    'itunes:owner': {
                        'itunes:name': 'Name',
                        'itunes:email': 'Email'
                    }
                },
                {
                    _name: 'itunes:image',
                    _attrs: {
                        href: 'image.jpg'
                    }
                },
                {
                    _name: 'itunes:category',
                    _attrs: {
                        text: 'Technology'
                    },
                    _content: {
                        _name: 'itunes:category',
                        _attrs: {
                            text: 'Gadgets'
                        }
                    }
                },
                {
                    _name: 'itunes:category',
                    _attrs: {
                        text: 'TV &amp; Film'
                    }
                },
                {
                    item: [
                        {
                            title: 'Podcast Title'
                        },
                        {
                            'itunes:author': 'Author'
                        },
                        {
                            'itunes:subtitle': 'Subtitle'
                        },
                        {
                            'itunes:summary': 'Summary'
                        },
                        {
                            'itunes:image': 'image.jpg'
                        },
                        {
                            _name: 'enclosure',
                            _attrs: {
                                url: 'http://example.com/podcast.m4a',
                                length: '8727310',
                                type: 'audio/x-m4a'
                            }
                        },
                        {
                            guid: 'http://example.com/archive/aae20050615.m4a'
                        },
                        {
                            pubDate: 'Wed, 15 Jun 2011 19:00:00 GMT'
                        },
                        {
                            'itunes:duration': '7:04'
                        },
                        {
                            'itunes:keywords': 'salt, pepper, shaker, exciting'
                        }
                    ]
                },
                {
                    item: [
                        {
                            title: 'Podcast2 Title'
                        },
                        {
                            'itunes:author': 'Author2'
                        },
                        {
                            'itunes:subtitle': 'Subtitle2'
                        },
                        {
                            'itunes:summary': 'Summary2'
                        },
                        {
                            'itunes:image': 'image2.jpg'
                        },
                        {
                            _name: 'enclosure',
                            _attrs: {
                                url: 'http://example.com/podcast2.m4a',
                                length: '655555',
                                type: 'audio/x-m4a'
                            }
                        },
                        {
                            guid: 'http://example.com/archive/aae2.m4a'
                        },
                        {
                            pubDate: 'Wed, 15 Jul 2011 19:00:00 GMT'
                        },
                        {
                            'itunes:duration': '11:20'
                        },
                        {
                            'itunes:keywords': 'foo, bar'
                        }
                    ]
                }
            ]
        }
    },
    xmlOptions
);

Output:

<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" version="2.0">
  <channel>
    <title>Title</title>
    <link>google.com</link>
    <language>en-us</language>
    <copyright>Copyright 2011</copyright>
    <itunes:subtitle>Subtitle</itunes:subtitle>
    <itunes:author>Author</itunes:author>
    <itunes:summary>Summary</itunes:summary>
    <description>Description</description>
    <itunes:owner>
      <itunes:name>Name</itunes:name>
      <itunes:email>Email</itunes:email>
    </itunes:owner>
    <itunes:image href="image.jpg"/>
    <itunes:category text="Technology">
      <itunes:category text="Gadgets"/>
    </itunes:category>
    <itunes:category text="TV &amp; Film"/>
    <item>
      <title>Podcast Title</title>
      <itunes:author>Author</itunes:author>
      <itunes:subtitle>Subtitle</itunes:subtitle>
      <itunes:summary>Summary</itunes:summary>
      <itunes:image>image.jpg</itunes:image>
      <enclosure url="http://example.com/podcast.m4a" length="8727310" type="audio/x-m4a"/>
      <guid>http://example.com/archive/aae20050615.m4a</guid>
      <pubDate>Wed, 15 Jun 2011 19:00:00 GMT</pubDate>
      <itunes:duration>7:04</itunes:duration>
      <itunes:keywords>salt, pepper, shaker, exciting</itunes:keywords>
    </item>
    <item>
      <title>Podcast2 Title</title>
      <itunes:author>Author2</itunes:author>
      <itunes:subtitle>Subtitle2</itunes:subtitle>
      <itunes:summary>Summary2</itunes:summary>
      <itunes:image>image2.jpg</itunes:image>
      <enclosure url="http://example.com/podcast2.m4a" length="655555" type="audio/x-m4a"/>
      <guid>http://example.com/archive/aae2.m4a</guid>
      <pubDate>Wed, 15 Jul 2011 19:00:00 GMT</pubDate>
      <itunes:duration>11:20</itunes:duration>
      <itunes:keywords>foo, bar</itunes:keywords>
    </item>
  </channel>
</rss>

Example 11: Custom filter for XML entities, or whatever

const xmlOptions = {
    contentReplacements: {
        '<': '&lt;',
        '>': '&gt;',
        '"': '&quot;',
        "'": '&apos;',
        '&': '&amp;'
    }
};

toXML(
    {
        foo: '<a>',
        bar: '"b"',
        baz: "'&whee'"
    },
    xmlOptions
);

Output:

<foo>&lt;a&gt;</foo><bar>&quot;b&quot;</bar><baz>&apos;&amp;whee&apos;</baz>

Example 11b: Custom filter for XML attributes

const xmlOptions = {
    attributeReplacements: {
        '<': '&lt;',
        '>': '&gt;',
        '"': '&quot;',
        "'": '&apos;',
        '&': '&amp;'
    }
};

toXML(
    {
        _name: 'foo',
        _attrs: { a: '<"\'&"foo>' }
    },
    xmlOptions
);

Output:

<foo a="&lt;&quot;&apos;&amp;&quot;foo&gt;"/>

Example 12: Avoiding self-closing tags

If you don't want self-closing tags, you can pass in a special config option selfCloseTags:

const xmlOptions = {
    selfCloseTags: false
};

toXML(
    {
        foo: '',
        bar: undefined
    },
    xmlOptions
);

Output:

<foo></foo><bar>whee</bar>

Example 13: Custom XML header

const xmlOptions = {
    header: '<?xml version="1.0" encoding="UTF-16" standalone="yes"?>'
};

toXML(
    {
        foo: 'bar'
    },
    xmlOptions
);

Output:

<?xml version="1.0" encoding="UTF-16" standalone="yes"?><foo>bar</foo><foo2>bar2</foo2>

Example 14: Emoji attribute support (needed for AMP)

toXML({
    html: {
        _attrs: {
            'โšก': true
        }
    }
});

Output:

<html โšก/>

Example 15: Duplicate attribute key support

toXML({
    html: {
        _attrs: [
            {
                lang: 'en'
            },
            {
                lang: 'klingon'
            }
        ]
    }
});

Output:

<html lang="en" lang="klingon"/>

Example 16: XML comments

toXML(
    {
        _comment: 'Some important comment',
        a: {
            b: [1, 2, 3]
        }
    },
    { indent: '    ' }
);

Output:

<!-- Some important comment -->
<a>
    <b>1</b>
    <b>2</b>
    <b>3</b>
</a>

Example 17: Multiple XML comments

toXML(
    [
        { _comment: 'Some important comment' },
        { _comment: 'This is a very long comment!' },
        { _comment: 'More important exposition!' },
        { a: { b: [1, 2, 3] } }
    ],
    { indent: '    ' }
);

Output:

<!-- Some important comment -->
<!-- This is a very long comment! -->
<!-- More important exposition! -->
<a>
    <b>1</b>
    <b>2</b>
    <b>3</b>
</a>

License

MIT

jstoxml's People

Contributors

bgschaap avatar davidcalhoun avatar dependabot[bot] avatar fdawgs avatar picohz avatar robertrossmann avatar sethmcl 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

jstoxml's Issues

Required escaping missing in version 2

Hi, v2 doesn't escape the '&' sign everywhere, but '&' needs to be always escaped

For example:
"capacit&eacute;"

Result in v1 (as it should be):
"capacit&amp;eacute;"

Result in v2 is unchanged, not escaped

In v1 I use these filters:

'<' : '&lt;',
'>' : '&gt;',
'"' : '&quot;',
'\'': '&apos;',
'&' : '&amp;'

In v2 using the same settings doesn't work

For reference see:
https://www.freeformatter.com/xml-escape.html

Related to #41

I added some checks to make sure things won't be double-encoded.

Maybe this caused the problem?

_content label doesn't work well -- alway pushed at the end if there are other labels

If I have an obj like this:

const obj = {
   label: 1
}

const obj2 = { 
    parentTag: {
      _content: obj,
      otherChildContentTagLikeThis: {
        nestedTag1: 'foo',
        nestedTag2: 'bar',
        nestedTag3: 69
    }
  }
}

The parsed xml doesn't respect the order of the element I declared. It is parsed as:

<parentTag>
      <otherChildContentTagLikeThis>
            <nestedTag1>foo</nestedTag1>
            <nestedTag2>bar</nestedTag2>
            <nestedTag3>69</nestedTag3>
      </otherChildContentTagLikeThis>
      <label>1</label>
</parentTag>

Maybe that isn't the pourpose of the _content label but I think that should be fixed in any way.

Another thing, If I try to push the other obj inside the _content label at this point is it parsed as

<parentTag>
  <_content>
      content of the two previous objs
  </_content>  
<parentTag>

Error upgrading to 1.1.x

I tried upgrading from 0.2.4 to 1.1.1 and received the following error:

[4/4] ๐Ÿ“ƒ  Rebuilding all packages...
[-/7] โก€ waiting...
[-/7] โก€ waiting...
[7/7] โก€ jstoxml: ./install.sh: line 5: /Users/myuser/myproject/node_modules/jstoxml/node_modules/.bin/babel: No such file or directory
[4/7] โก€ keytar: clang: warning: libstdc++ is deprecated; move to libc++ with a minimum deployment target of OS X 10.9 [-Wdeprecated]
error /Users/myuser/myproject/node_modules/jstoxml: Command failed.
Exit code: 127
Command: ./install.sh
Arguments: 
Directory: /Users/myuser/myproject/node_modules/jstoxml
Output:

OS: macOS 10.12.6
Package manager: yarn 1.0.2
Node: node 6.9.1

It looks like this issue stems from your install.sh script attempting to invoke babel, however, babel-cli is only listed as a devDependency. I think if you're going to use it in a postinstall hook, it needs to be listed as either a dependency or peerDependency.

Problem with self-closing tags

Hello, I have problem with self-closing tags. When I use _selfCloseTag parameter with value false, It's doesn't work, empty tags don't change to and still stay like . Please check dist/jstoxml.js line 345, I think here must be conf._selfCloseTag.

Skip indent for complex xml (or related solution)

I am using your library to write bpmn and need an effective way to write CDATA. Right now, when I output my xml, it puts an indent after a CDATA value. I noticed in the code that there is a flag for marking complex xml values such that if there is an open bracket in the value, then an indent and a newline will be added to the document. Can there be an option to get around this? Possibly an '_xmlIsSimple' xml option? Or potentially some way to create CDATA without writing out the open and close brackets myself?

Support for Array

Hey,

first of all I love your package, the way you handle the reserved characters in XML is just beautiful!

But I do have an issue when converting a JSON that contains an array. In detail I have the following JSON:

{ "autoFill": [ { "id": 53, "description": "Brochure" }, { "id": 5, "description": "Digital - Booklet" } ]}

and I expect the following XML:

<?xml version="1.0" encoding="UTF-8" standalone="no" ?> <MultiPress> <autoFill> <id>53</id> <description>Brochure</description> </autoFill> <autoFill> <id>5</id> <description>Digital - Booklet</description> </autoFill> </MultiPress>

But I am getting this:

<?xml version="1.0" encoding="UTF-8" standalone="no" ?> <MultiPress> <autoFill> <id>53</id> <description>Brochure</description> <id>5</id> <description>Digital - Booklet</description> </autoFill> </MultiPress>

is there anything I am missing here?

Not working properly for Arrays

I'm trying to convert the following JSON to XML. But it canโ€™t parse arrays correctly.
Here's a current versus desired result:
https://gist.github.com/baharalidurrani/6ae5951337488ae8659c04da144a67ad/revisions

Screenshot 2023-01-18 at 16 48 03

If we look at the screenshot above we can clearly see that we lost some tags on line numbers 29,30 and 41,42.
{
  "MESSAGE": {
    "ABOUT_VERSIONS": {
      "ABOUT_VERSION": [
        {
          "CreatedDatetime": "2019-02-22T14:24:17Z"
        }
      ]
    },
    "DEAL_SETS": {
      "DEAL_SET": [
        {
          "DEALS": {
            "DEAL": [
              {
                "ASSETS": {
                  "ASSET": [
                    {
                      "ASSET_DETAIL": {
                        "AssetAccountIdentifier": 123456,
                        "AssetCashOrMarketValueAmount": 12000,
                        "AssetType": "CheckingAccount"
                      },
                      "ASSET_HOLDER": {
                        "NAME": {
                          "FullName": "Bank of Noosh"
                        }
                      }
                    },
                    {
                      "ASSET_DETAIL": {
                        "AssetAccountIdentifier": 1234567,
                        "AssetCashOrMarketValueAmount": 100000,
                        "AssetType": "CertificateOfDepositTimeDeposit"
                      },
                      "ASSET_HOLDER": {
                        "NAME": {
                          "FullName": "Bank of Noosh"
                        }
                      }
                    },
                    {
                      "ASSET_DETAIL": {
                        "AssetAccountIdentifier": 6465456,
                        "AssetCashOrMarketValueAmount": 50000,
                        "AssetType": "TrustAccount",
                        "FundsSourceType": "Other",
                        "FundsSourceTypeOtherDescription": "Trust"
                      },
                      "ASSET_HOLDER": {
                        "NAME": {
                          "FullName": "Fidelity Investments"
                        }
                      }
                    },
                    {
                      "ASSET_DETAIL": {
                        "AssetAccountIdentifier": 55555,
                        "AssetCashOrMarketValueAmount": 120000,
                        "AssetType": "MutualFund"
                      },
                      "ASSET_HOLDER": {
                        "NAME": {
                          "FullName": "UBS"
                        }
                      }
                    }
                  ]
                },
                "COLLATERALS": {
                  "COLLATERAL": [
                    {
                      "SUBJECT_PROPERTY": {
                        "ADDRESS": {
                          "AddressLineText": "10655 Birch St",
                          "CityName": "Burbank",
                          "PostalCode": "915021234",
                          "StateCode": "CA"
                        },
                        "PROPERTY_DETAIL": {
                          "AttachmentType": "Attached",
                          "CommunityPropertyStateIndicator": true,
                          "ConstructionMethodType": "SiteBuilt",
                          "FinancedUnitCount": 1,
                          "PropertyEstateType": "FeeSimple",
                          "PropertyExistingCleanEnergyLienIndicator": false,
                          "PropertyInProjectIndicator": false,
                          "PropertyMixedUsageIndicator": false,
                          "PropertyStructureBuiltYear": 1965,
                          "PropertyUsageType": "PrimaryResidence",
                          "PUDIndicator": false
                        },
                        "PROPERTY_VALUATIONS": {
                          "PROPERTY_VALUATION": {
                            "PROPERTY_VALUATION_DETAIL": {
                              "PropertyValuationAmount": 340000
                            }
                          }
                        },
                        "SALES_CONTRACTS": {
                          "SALES_CONTRACT": {
                            "SALES_CONCESSIONS": {
                              "SALES_CONCESSION": {
                                "SalesConcessionAmount": 1000
                              }
                            },
                            "SALES_CONTRACT_DETAIL": {
                              "SalesContractAmount": 340000
                            }
                          }
                        }
                      }
                    }
                  ]
                },
                "LIABILITIES": {
                  "LIABILITY": [
                    {
                      "LIABILITY_DETAIL": {
                        "LiabilityAccountIdentifier": 98,
                        "LiabilityExclusionIndicator": false,
                        "LiabilityMonthlyPaymentAmount": 44,
                        "LiabilityPayoffStatusIndicator": false,
                        "LiabilityRemainingTermMonthsCount": 10,
                        "LiabilityType": "Revolving",
                        "LiabilityUnpaidBalanceAmount": 437
                      },
                      "LIABILITY_HOLDER": {
                        "NAME": {
                          "FullName": "HEMLOCKS"
                        }
                      }
                    },
                    {
                      "LIABILITY_DETAIL": {
                        "LiabilityAccountIdentifier": "291443C81189",
                        "LiabilityExclusionIndicator": false,
                        "LiabilityMonthlyPaymentAmount": 425,
                        "LiabilityPayoffStatusIndicator": false,
                        "LiabilityRemainingTermMonthsCount": 35,
                        "LiabilityType": "Installment",
                        "LiabilityUnpaidBalanceAmount": 14748
                      },
                      "LIABILITY_HOLDER": {
                        "NAME": {
                          "FullName": "HILLSIDE BANK"
                        }
                      }
                    }
                  ]
                },
                "LOANS": {
                  "LOAN": [
                    {
                      "AMORTIZATION": {
                        "AMORTIZATION_RULE": {
                          "AmortizationType": "Fixed",
                          "LoanAmortizationPeriodCount": 360,
                          "LoanAmortizationPeriodType": "Month"
                        }
                      },
                      "CLOSING_INFORMATION": {
                        "CLOSING_ADJUSTMENT_ITEMS": {
                          "CLOSING_ADJUSTMENT_ITEM": {
                            "CLOSING_ADJUSTMENT_ITEM_DETAIL": {
                              "ClosingAdjustmentItemAmount": 1000,
                              "ClosingAdjustmentItemType": "LenderCredit"
                            }
                          }
                        },
                        "CLOSING_INFORMATION_DETAIL": {
                          "CashFromBorrowerAtClosingAmount": 28800
                        }
                      },
                      "DOCUMENT_SPECIFIC_DATA_SETS": {
                        "DOCUMENT_SPECIFIC_DATA_SET": {
                          "URLA": {
                            "URLA_DETAIL": {
                              "ApplicationSignedByLoanOriginatorDate": "2019-01-07",
                              "EstimatedClosingCostsAmount": 6000,
                              "PrepaidItemsEstimatedAmount": 2750
                            },
                            "URLA_TOTAL": {
                              "EXTENSION": {
                                "OTHER": {
                                  "URLA_TOTAL_EXTENSION": {
                                    "URLATotalSellerCreditsAmount": 4750
                                  }
                                }
                              }
                            }
                          }
                        }
                      },
                      "HMDA_LOAN": {
                        "HMDA_LOAN_DETAIL": {
                          "HMDA_HOEPALoanStatusIndicator": false
                        }
                      },
                      "HOUSING_EXPENSES": {
                        "HOUSING_EXPENSE": [
                          {
                            "HousingExpensePaymentAmount": 1475.82,
                            "HousingExpenseTimingType": "Proposed",
                            "HousingExpenseType": "FirstMortgagePrincipalAndInterest"
                          },
                          {
                            "HousingExpensePaymentAmount": 50,
                            "HousingExpenseTimingType": "Proposed",
                            "HousingExpenseType": "MIPremium"
                          },
                          {
                            "HousingExpensePaymentAmount": 75,
                            "HousingExpenseTimingType": "Proposed",
                            "HousingExpenseType": "HomeownersInsurance"
                          },
                          {
                            "HousingExpensePaymentAmount": 165,
                            "HousingExpenseTimingType": "Proposed",
                            "HousingExpenseType": "RealEstateTax"
                          },
                          {
                            "HousingExpensePaymentAmount": 365,
                            "HousingExpenseTimingType": "Proposed",
                            "HousingExpenseType": "HomeownersAssociationDuesAndCondominiumFees"
                          },
                          {
                            "HousingExpensePaymentAmount": 100,
                            "HousingExpenseTimingType": "Proposed",
                            "HousingExpenseType": "Other"
                          }
                        ]
                      },
                      "LOAN_DETAIL": {
                        "ApplicationReceivedDate": "2019-01-06",
                        "BalloonIndicator": false,
                        "BelowMarketSubordinateFinancingIndicator": false,
                        "BorrowerCount": 1,
                        "BuydownTemporarySubsidyFundingIndicator": false,
                        "ConstructionLoanIndicator": false,
                        "InterestOnlyIndicator": false,
                        "NegativeAmortizationIndicator": false,
                        "PrepaymentPenaltyIndicator": false
                      },
                      "LOAN_IDENTIFIERS": {
                        "LOAN_IDENTIFIER": [
                          {
                            "LoanIdentifier": "DI-C01_v3.4",
                            "LoanIdentifierType": "LenderLoan"
                          }
                        ]
                      },
                      "PURCHASE_CREDITS": {
                        "PURCHASE_CREDIT": [
                          {
                            "PurchaseCreditAmount": 10000,
                            "PurchaseCreditType": "EarnestMoney"
                          },
                          {
                            "PurchaseCreditAmount": 4200,
                            "PurchaseCreditType": "LeasePurchaseFund"
                          }
                        ]
                      },
                      "TERMS_OF_LOAN": {
                        "BaseLoanAmount": 300000,
                        "LienPriorityType": "FirstLien",
                        "LoanPurposeType": "Purchase",
                        "MortgageType": "Conventional",
                        "NoteRatePercent": 4.25
                      }
                    }
                  ]
                },
                "PARTIES": {
                  "PARTY": [
                    {
                      "INDIVIDUAL": {
                        "CONTACT_POINTS": {
                          "CONTACT_POINT": [
                            {
                              "CONTACT_POINT_EMAIL": {
                                "ContactPointEmailValue": "[email protected]"
                              }
                            },
                            {
                              "CONTACT_POINT_TELEPHONE": {
                                "ContactPointTelephoneValue": "8182222222"
                              }
                            },
                            {
                              "CONTACT_POINT_DETAIL": {
                                "ContactPointRoleType": "Home"
                              }
                            }
                          ]
                        },
                        "NAME": {
                          "FirstName": "Ken",
                          "LastName": "Customer",
                          "MiddleName": "N",
                          "SuffixName": "JR"
                        }
                      },
                      "ADDRESSES": {
                        "ADDRESS": {
                          "AddressLineText": "10655 Birch St",
                          "AddressType": "Mailing",
                          "CityName": "Burbank",
                          "CountryCode": "US",
                          "PostalCode": 915021234,
                          "StateCode": "CA"
                        }
                      },
                      "LANGUAGES": {
                        "LANGUAGE": {
                          "LanguageCode": "eng",
                          "EXTENSION": {
                            "OTHER": {
                              "LANGUAGE_EXTENSION": {
                                "LanguageRefusalIndicator": false
                              }
                            }
                          }
                        }
                      },
                      "ROLES": {
                        "ROLE": [
                          {
                            "BORROWER": {
                              "BORROWER_DETAIL": {
                                "BorrowerBirthDate": "1966-07-04",
                                "BorrowerTotalMortgagedPropertiesCount": 1,
                                "CommunityPropertyStateResidentIndicator": true,
                                "DependentCount": 0,
                                "DomesticRelationshipIndicator": true,
                                "DomesticRelationshipType": "DomesticPartnership",
                                "MaritalStatusType": "Unmarried"
                              },
                              "COUNSELING": {
                                "COUNSELING_EVENTS": {
                                  "COUNSELING_EVENT": [
                                    {
                                      "COUNSELING_EVENT_DETAIL": {
                                        "CounselingConfirmationIndicator": false,
                                        "CounselingType": "Counseling"
                                      }
                                    },
                                    {
                                      "COUNSELING_EVENT_DETAIL": {
                                        "CounselingConfirmationIndicator": false,
                                        "CounselingType": "Education"
                                      }
                                    }
                                  ]
                                }
                              },
                              "CURRENT_INCOME": {
                                "CURRENT_INCOME_ITEMS": {
                                  "CURRENT_INCOME_ITEM": [
                                    {
                                      "CURRENT_INCOME_ITEM_DETAIL": {
                                        "CurrentIncomeMonthlyTotalAmount": 10000,
                                        "EmploymentIncomeIndicator": true,
                                        "IncomeType": "Base"
                                      }
                                    },
                                    {
                                      "CURRENT_INCOME_ITEM_DETAIL": {
                                        "CurrentIncomeMonthlyTotalAmount": 1000,
                                        "EmploymentIncomeIndicator": true,
                                        "IncomeType": "Overtime"
                                      }
                                    },
                                    {
                                      "CURRENT_INCOME_ITEM_DETAIL": {
                                        "CurrentIncomeMonthlyTotalAmount": 750,
                                        "EmploymentIncomeIndicator": true,
                                        "IncomeType": "Bonus"
                                      }
                                    },
                                    {
                                      "CURRENT_INCOME_ITEM_DETAIL": {
                                        "CurrentIncomeMonthlyTotalAmount": 1000,
                                        "EmploymentIncomeIndicator": false,
                                        "IncomeType": "DividendsInterest"
                                      }
                                    },
                                    {
                                      "CURRENT_INCOME_ITEM_DETAIL": {
                                        "CurrentIncomeMonthlyTotalAmount": 100,
                                        "EmploymentIncomeIndicator": false,
                                        "IncomeType": "AutomobileAllowance"
                                      }
                                    },
                                    {
                                      "CURRENT_INCOME_ITEM_DETAIL": {
                                        "CurrentIncomeMonthlyTotalAmount": 250,
                                        "EmploymentIncomeIndicator": false,
                                        "IncomeType": "NotesReceivableInstallment"
                                      }
                                    },
                                    {
                                      "CURRENT_INCOME_ITEM_DETAIL": {
                                        "CurrentIncomeMonthlyTotalAmount": 1000,
                                        "EmploymentIncomeIndicator": false,
                                        "IncomeType": "Trust"
                                      }
                                    }
                                  ]
                                }
                              },
                              "DECLARATION": {
                                "DECLARATION_DETAIL": {
                                  "BankruptcyIndicator": false,
                                  "CitizenshipResidencyType": "USCitizen",
                                  "HomeownerPastThreeYearsType": "No",
                                  "IntentToOccupyType": "Yes",
                                  "OutstandingJudgmentsIndicator": false,
                                  "PartyToLawsuitIndicator": false,
                                  "PresentlyDelinquentIndicator": false,
                                  "PriorPropertyDeedInLieuConveyedIndicator": false,
                                  "PriorPropertyForeclosureCompletedIndicator": false,
                                  "PriorPropertyShortSaleCompletedIndicator": false,
                                  "PropertyProposedCleanEnergyLienIndicator": false,
                                  "UndisclosedBorrowedFundsIndicator": false,
                                  "UndisclosedComakerOfNoteIndicator": false,
                                  "UndisclosedCreditApplicationIndicator": false,
                                  "UndisclosedMortgageApplicationIndicator": false,
                                  "EXTENSION": {
                                    "OTHER": {
                                      "DECLARATION_DETAIL_EXTENSION": {
                                        "SpecialBorrowerSellerRelationshipIndicator": false
                                      }
                                    }
                                  }
                                }
                              },
                              "EMPLOYERS": {
                                "EMPLOYER": {
                                  "LEGAL_ENTITY": {
                                    "CONTACTS": {
                                      "CONTACT": {
                                        "CONTACT_POINTS": {
                                          "CONTACT_POINT": {
                                            "CONTACT_POINT_TELEPHONE": {
                                              "ContactPointTelephoneValue": 8183323332
                                            }
                                          }
                                        }
                                      }
                                    },
                                    "LEGAL_ENTITY_DETAIL": {
                                      "FullName": "National Consulting"
                                    }
                                  },
                                  "ADDRESS": {
                                    "AddressLineText": "1236 Main St",
                                    "CityName": "Burbank",
                                    "PostalCode": 915021234,
                                    "StateCode": "CA"
                                  },
                                  "EMPLOYMENT": {
                                    "EmploymentBorrowerSelfEmployedIndicator": false,
                                    "EmploymentClassificationType": "Primary",
                                    "EmploymentPositionDescription": "Consultant",
                                    "EmploymentStartDate": "2003-02-15",
                                    "EmploymentStatusType": "Current",
                                    "EmploymentTimeInLineOfWorkMonthsCount": 252,
                                    "SpecialBorrowerEmployerRelationshipIndicator": false,
                                    "EXTENSION": {
                                      "OTHER": {
                                        "EMPLOYMENT_EXTENSION": {
                                          "ForeignIncomeIndicator": false,
                                          "SeasonalIncomeIndicator": false
                                        }
                                      }
                                    }
                                  }
                                }
                              },
                              "GOVERNMENT_MONITORING": {
                                "GOVERNMENT_MONITORING_DETAIL": {
                                  "HMDAEthnicityRefusalIndicator": false,
                                  "HMDAGenderRefusalIndicator": false,
                                  "HMDARaceRefusalIndicator": false,
                                  "EXTENSION": {
                                    "OTHER": {
                                      "GOVERNMENT_MONITORING_DETAIL_EXTENSION": {
                                        "ApplicationTakenMethodType": "FaceToFace",
                                        "HMDAGenderType": "Male"
                                      }
                                    }
                                  }
                                },
                                "HMDA_RACES": {
                                  "HMDA_RACE": {
                                    "HMDA_RACE_DETAIL": {
                                      "HMDARaceType": "BlackOrAfricanAmerican"
                                    }
                                  }
                                },
                                "EXTENSION": {
                                  "OTHER": {
                                    "GOVERNMENT_MONITORING_EXTENSION": {
                                      "HMDA_ETHNICITIES": {
                                        "HMDA_ETHNICITY": {
                                          "HMDAEthnicityType": "HispanicOrLatino"
                                        }
                                      }
                                    }
                                  }
                                }
                              },
                              "RESIDENCES": {
                                "RESIDENCE": {
                                  "ADDRESS": {
                                    "AddressLineText": "10655 Birch St",
                                    "CityName": "Burbank",
                                    "PostalCode": 915021234,
                                    "StateCode": "CA"
                                  },
                                  "LANDLORD": {
                                    "LANDLORD_DETAIL": {
                                      "MonthlyRentAmount": 3500
                                    }
                                  },
                                  "RESIDENCE_DETAIL": {
                                    "BorrowerResidencyBasisType": "Rent",
                                    "BorrowerResidencyDurationMonthsCount": 43,
                                    "BorrowerResidencyType": "Current"
                                  }
                                }
                              }
                            },
                            "ROLE_DETAIL": {
                              "PartyRoleType": "Borrower"
                            }
                          }
                        ]
                      },
                      "TAXPAYER_IDENTIFIERS": {
                        "TAXPAYER_IDENTIFIER": {
                          "TaxpayerIdentifierType": "SocialSecurityNumber",
                          "TaxpayerIdentifierValue": 500507000
                        }
                      }
                    },
                    {
                      "INDIVIDUAL": {
                        "NAME": {
                          "FullName": "Kenneth Customer"
                        }
                      },
                      "ROLES": {
                        "ROLE": [
                          {
                            "PROPERTY_OWNER": {
                              "PropertyOwnerStatusType": "Proposed",
                              "RelationshipVestingType": "Individual"
                            },
                            "ROLE_DETAIL": {
                              "PartyRoleType": "PropertyOwner"
                            }
                          }
                        ]
                      }
                    },
                    {
                      "LEGAL_ENTITY": {
                        "LEGAL_ENTITY_DETAIL": {
                          "FullName": "ABC Mortgage"
                        }
                      },
                      "ADDRESSES": {
                        "ADDRESS": {
                          "AddressLineText": "412 H St, NW",
                          "CityName": "Washington",
                          "PostalCode": 200121234,
                          "StateCode": "DC"
                        }
                      },
                      "ROLES": {
                        "ROLE": [
                          {
                            "LICENSES": {
                              "LICENSE": [
                                {
                                  "LICENSE_DETAIL": {
                                    "LicenseIdentifier": "123456789111"
                                  }
                                }
                              ]
                            },
                            "ROLE_DETAIL": {
                              "PartyRoleType": "LoanOriginationCompany"
                            }
                          }
                        ]
                      }
                    },
                    {
                      "INDIVIDUAL": {
                        "CONTACT_POINTS": {
                          "CONTACT_POINT": {
                            "CONTACT_POINT_TELEPHONE": {
                              "ContactPointTelephoneValue": 2023333333
                            }
                          }
                        },
                        "NAME": {
                          "FirstName": "John",
                          "LastName": "Done"
                        }
                      },
                      "ROLES": {
                        "ROLE": [
                          {
                            "LICENSES": {
                              "LICENSE": [
                                {
                                  "LICENSE_DETAIL": {
                                    "LicenseIdentifier": 123456789012
                                  }
                                }
                              ]
                            },
                            "ROLE_DETAIL": {
                              "PartyRoleType": "LoanOriginator"
                            }
                          }
                        ]
                      }
                    }
                  ]
                },
                "RELATIONSHIPS": {
                  "RELATIONSHIP": ["", "", "", "", "", "", "", "", ""]
                }
              }
            ]
          }
        }
      ]
    }
  }
}

Handling duplicate tag names

Hi guys, I have a requirement where duplicate tag names should be displayed in one parent tag.

for example (input JSON):

let input = {
	"toAddress": {
		"street": "Street name first line",
		"street2": "",
		"zip": "70286"
	}
}

I handled it like below but it is displaying an empty content instead of displaying both Address lines.

{
_name: 'Receiver',
_content: {
	PhysicalAddress: {
	  AddressLine: input['toAddress']['street'],
	  AddressLine: input['toAddress']['street2'],
	  PostalCode: input['toAddress']['zip']
	}
}

Results (WRONG):

<PhysicalAddress>
   <AddressLine></AddressLine>
   <PostalCode>12345</PostalCode>
</PhysicalAddress>

Expected Results:

<PhysicalAddress>
   <AddressLine>Street name first line</AddressLine>
   <AddressLine />
   <PostalCode>12345</PostalCode>
</PhysicalAddress>

I think, it overwrites the latest value of <AddressLine> tag. What is the correct way of doing this ?

Clarify documentation around browser use

Attempting a super-simple example without webpack or rollup, the instructed line

// Browser
import { toXML } from 'jstoxml';

produces a SyntaxError: Unexpected token { even in Chrome 74, which seems like it should support native import.

Using <script type="module" src="js/jstoxml.js"> (where that jstoxml.js comes from the dist following npm install) results in an error TypeError: Cannot set property 'jstoxml' of undefined at jstoxml.js:11 at jstoxml.js:13.

Clearer documentation for how to use this package in the browser, explicitly stating any requirements like webpack/rollup, would be an improvement!

Header encoding

How do you guys change the encoding of the header?

for example:
<?xml version="1.0" encoding="iso-8859-1"?>

Thank you.

tag with attributes not working

I am trying to generate

<Amt Ccy="EUR">158.4</Amt> (as part of a larger SEPA payment xml)

Following the examples 4 and 7, I came up with
this.creditTransferTxXml.CdtTrfTxInf.Amt = {_attrs: {Ccy:SEPAInfo.payment.currency}, _content: SEPAInfo.payment.amount.toString()};

However, this produces
<Amt Ccy="EUR"><_content>158.4</_content><Amt>

I know the example #4 had only one tag, so I mixed it with example #7 and added a string instead of an object to the _content object.

I tried to do it with two steps(first add attribute, then add content), but couldn't figure out that either

Tried it with name attrtibute as well:
this.creditTransferTxXml.CdtTrfTxInf.Amt = {_name: 'Amt', _attrs: {Ccy:SEPAInfo.payment.currency}, _content: SEPAInfo.payment.amount.toString()};

However, this produced what I want, and then an extra tag wrapped around it
<Amt><Amt Ccy="EUR">158.4</Amt></Amt>

Syntactically it tries to add and Amt element to existing Amt element. I'm just trying to modfy the existing one

0-depth issues

There's some bugs in the code related to code at 0 depth outputting unexpected results, e.g.

const table = [
  {
    row: 'bar'
  },
  {
    row: 'bar2'
  }
];
const xmlOptions = {
  header: '<?xml version="1.0" encoding="UTF-16" standalone="yes"?>',
  indent: ' '
};

console.log(toXML(table, xmlOptions));

Outputs this:

<?xml version="1.0" encoding="UTF-16" standalone="yes"?>
<?xml version="1.0" encoding="UTF-16" standalone="yes"?>
<row>bar</row>
<?xml version="1.0" encoding="UTF-16" standalone="yes"?>
<row>bar2</row>

When it should output this:

<?xml version="1.0" encoding="UTF-16" standalone="yes"?>
<row>bar</row>
<row>bar2</row>

Reference: https://stackoverflow.com/questions/68130732/cant-add-header-info-properly-using-jstoxml

text content is not encoded

When the JSON contains an & in the text content, jstoxml doesn't encode the & as & amp; in the resulting XML.
Ditto for other XML characters which should be encoded (eg <)

_selfCloseTag not working

Passing the {_selfCloseTag: false} option has no effect, tags are always self closed.

I think the issue may be in jstoxml.js line 326. The check is against the object looking for the _selfCloseTag attribute, not the config object.
I'd send a pull request but I don't understand the code well enough to be sure this doesn't break something else.

iterated tag with multiple values.

Hi, I need some help with the tag iteration:

for now my output comes like this : -

<invoice1>
  <invoice>a</invoice>
  <currentTime>
    <foo>1</foo>
    <foo>2</foo>
    <foo>3</foo>
  </currentTime>
  <foo2>a</foo2>
</invoice1>

I want it to be converted as : -

<invoice1>
   <invoice>a</invoice>
   <currentTime>
     <foo>1</foo>
  </currentTime>
  <currentTime>
    <foo>2</foo>
  </currentTime>
  <currentTime>
    <foo>3</foo>
  </currentTime>
  <foo2>a</foo2>
 </invoice1>

-Note - foo is a dynamic field. Not aware of the values in advance.

Could anyone please help me with this?

Thanks in advance.

XSD validation and linting

This is a big ask: apologies in advance ๐Ÿ˜…

In some cases, the XML being generated is supposed to conform to a given XSD. It would be great to validate the generated XML against a given XSD if the latter is passed.

Furthermore, XSD allows the specification of an xs:sequence that defines the ordering of child elements. Since jstoxml respects the given order of elements, this means the caller has to ensure that the given order respects the xs:sequence. It would be great to re-sort the input elements according to their sequence, if applicable. Here's an example of how I do it in my code. A generic solution would have to actually get this info from the XSD directly.

Thanks for reading!

Vulnerability: special characters should be escaped by default

By default, this package does not escape XML characters:

jstoxml.toXML({
  foo: '&'
}
// Output: <foo>&</foo>

This is dangerous behaviour, since it means invalid XML can be output, causing injection vulnerabilities.

As a user, I would expect this package to escape strings by default. If there is a need to disable escaping, this could be done in the config, but that should not be the default behaviour.

npm install fails

Tried to install this package and ./install.sh fails.

Using:

  • Linux Ubuntu
  • Root access using sudo

Support for Array

Hi, I like your library which is simple and straight-forward.

However, I was caught by surprised when the array being converted to xml.

const { toXML } = require('jstoxml')
console.log(toXML({ x: ['lat','lng'] })) // -> <x>latlng</x>
console.log(toXML({ x: [1,2,3] })) // -> <x>123</x>

The library will just concat all the values into a string instead of separating it, presumably like:

<x-arr name="x">
    <x>1</x>
    <x>2</x>
    <x>3</x>
</x-arr>

Is there anything that I missed?

Thanks in advance.

Including a closed tag

Is it possible to simply include a tag like <SomeItem /> as oppose to <SomeItem></SomeItem> which is what I get when I give an empty string.

Thank you

Boolean values and empty strings

Hello there!

I'm having issues when trying to convert objects with empty strings and boolean values, here is the test app.js:

jstoxml = require('jstoxml');

xml = jstoxml.toXML({
    foo: true,
    bar: '',
    foo2: false,
    ok: 'This is ok',
    ok2: 'false',
    ok3: 'true'
});

console.log(xml);

The "formatted" output is:

 barfoo2
 <ok>This is ok</ok>
 <ok2>false</ok2>
 <ok3>true</ok3>

But I 'm really convinced that the output shoud be more like:

 <foo>true</foo>
 <bar></bar>
 <foo2>false</foo2>
 <ok>This is ok</ok>
 <ok2>false</ok2>
 <ok3>true</ok3>

When using null the same problem happens.

New Line After each tag

How can I make new line after each tag? For example:
<a>A Value</a>
<b>B Value</b>
Instead of: <a>A Value</a><b>B Value</b>

I'm using "jstoxml": "^1.6.8"

node-gyp errors when installing

Hi, there are some node-gyp errors occurring when installing with the latest LTS version of Node. Could be related to a dependency.
image

Null attributes invalidating XML

After peeking this GitHub, I see a 2013 issue here #10 about this. However, in the case I want to describe. We get information from a source we cannot control, sometimes those are null.

Screen Shot 2022-03-04 at 8 39 35 AM

This causes the null property to be converted into text, then within the picture of a full XML document is invalid and thus rejected by the server with a vague exception.

Have you reconsidered accepting an option/config to disable this? I notice now vs 2013 you have a config object parameter that is optional, so it could gain a property to opt-in this handling to prevent compatibility issues with clients who prefer this existing behavior.

Thanks!

Publish new release to npm?

The 0.2.3 version on npm is very old, and includes the issue that causes the indent variable to leak (#21). This has since been fixed in this repository, but the version on npm still has that bug, and it's breaking our unit tests. Can a new version be published?

jstoxml incorrectly parsing nested json

I have valid json that is being output:

[{"ComputerSettings":{"TargetComputers":{"ComputerID":"285809"},"Setting":[{"Name":"critical","Value":592},{"Name":"exploits","Value":108},{"Name":"malwareKits","Value":1},{"Name":"moderate","Value":214},{"Name":"severe","Value":1904},{"Name":"total","Value":2710}]}}]

As you can see it is very complex. The XML that is returned is incorrect as it doesn't treat each name-value object as a separate xml node, but instead groups them. Here is the XML that is returned:

<ComputerSettings> <TargetComputers> <ComputerID>289072</ComputerID> </TargetComputers> <Setting> <Name>critical</Name> <Value>549</Value> <Name>exploits</Name> <Value>77</Value> <Name>malwareKits</Name> <Value>0</Value> <Name>moderate</Name> <Value>118</Value> <Name>severe</Name> <Value>1204</Value> <Name>total</Name> <Value>1871</Value> </Setting> </ComputerSettings>

When run through an online xml converter it converts correctly, with each entry looking like:

<ComputerSettings> <TargetComputers> <ComputerID>520698</ComputerID> </TargetComputers> <Setting> <Name>critical</Name> <Value>182</Value> </Setting> <Setting> <Name>exploits</Name> <Value>51</Value> </Setting> <Setting> <Name>malwareKits</Name> <Value>1</Value> </Setting> <Setting> <Name>moderate</Name> <Value>102</Value> </Setting> <Setting> <Name>severe</Name> <Value>1188</Value> </Setting> <Setting> <Name>total</Name> <Value>1472</Value> </Setting> </ComputerSettings>

Newlines not possible without indent

Currently it is not possible to add newlines after each tag when using an empty string as indent.

Suggestion: replace

const preIndentStr = indent && !isOutputStart ? '\n' : '';

with
const preIndentStr = (typeof indent === DATA_TYPES.STRING) && !isOutputStart ? '\n' : '';

change in behavior from 1.3.2 to 1.4.1

When an object contains an array(schema 1), the expected result in v.1.3.2 was one item with several sub items (schema 2). But now we have several items with one sub item each. (schema 3)

Schema 1 (data to convert):

var jstoxml = require("jstoxml")

jstoxml.toXML({Response: [
    {_name: "Play", _content: "first sound"},
    {_name: "Play", _content: "second sound"},
]});

Schema 2 (expected result):

<Response><Play>first sound</Play><Play>second sound</Play></Response>

Schema 3 (actual result):

<Response><Play>first sound</Play></Response><Response><Play>second sound</Play></Response>

Special value to ignore entry

Thanks for a very useful module!

I'm writing a fairly complex XML-generation program and there are cases where some attributes/elements may or may not be included, depending on the business logic. Right now, in order to ignore such an entry, I need to add an explicit condition that only adds the entry in case the condition is satisfied.

A simpler way to code would be to add the entry regardless, and reserve a special value, e.g. undefined, to instruct the XML generator to ignore the entry when the special value is found.

For example, instead of writing:

const snippet = [{
  'element1': 'hello',
}];
if (condition) {
  snippet.push({ 'element2': 'goodbye' });
}

one could write:

const snippet = [{
   'element1': 'hello'
}, {
   'element2': condition ? 'goodbye' : undefined
}];

Breaking change between 0.2.4 and 1.1.3

When consuming the 0.2.4 release through webpack 3 using babel-loader, a default module export is generated:
screen shot 2017-10-28 at 1 19 52 pm

Consuming the 1.1.3 release through the same version of webpack results in no default module export:
screen shot 2017-10-28 at 1 23 27 pm

This forces an imports change from:

import jstoxml from 'jstoxml';

to

import { toXML } from 'jstoxml';

Looking at the different method of computing module exports in the 1.x release line, it's not immediately clear to me why this is happening. (Maybe I'm just not up on my module semantics.)

Submitting this in case this behavior change wasn't intentional, but mostly as a documentation trail for others.

Replacement of non strings

Thanks for this helpful code!

But in line 17 I had to say 'return String(txt).replace โ€ฆ' because of an runtime error with numbers.

And different from your samples I had to use {filter:โ€ฆ} always for the &.

Why is your module better than others js to xml converters?

I need to build a relatively simple XML document with some hierarchy and some attributes from a JS object. I stand now before choosing one of these modules:
https://github.com/davidcalhoun/jstoxml
https://github.com/soldair/node-jsontoxml
https://github.com/QuickenLoans/node-easyxml
https://github.com/michaelkourlas/node-js2xmlparser

Why should I pick this module instead of one of the others?

Disclaimer: I posted the same question as an issue on the other repositories too.

Creating multiple headers

Hello,

Is it possible to create multiple headers like below?

<?xml version="1.0" encoding="UTF-8"?>
<?RETRONET TNT="123" TNTVAL="XX"?>
<person>
<fullname>ember dodge</fullname>
</person>

_attrs are ignored if an Array exists

tx for this lib.
I am having an issue when I am dealing with a combination of both _attar and []
for example, if I have this data structure, my _attrs are discarded and not added to final XML since an Array exists :

[
  {
    "_attrs": {
      "delay": "0",
      "duration": "2000",
      "easingId": "10"
    },
    "Glow": [
      {
        "_attrs": {
          "alpha": "1",
          "color": "255",
          "blur": "0",
          "resolution": "1",
          "blurX": "undefined",
          "blurY": "undefined",
          "strength": "undefined"
        }
      }
    ]
  },
  {
    "_attrs": {
      "delay": "0",
      "duration": "2000",
      "easingId": "10"
    },
    "Glow": [
      {
        "_attrs": {
          "alpha": "1",
          "color": "255",
          "blur": "0",
          "resolution": "1",
          "blurX": "undefined",
          "blurY": "undefined",
          "strength": "undefined"
        }
      }
    ]
  },
  {
    "_attrs": {
      "delay": "3",
      "duration": "1998",
      "easingId": "20"
    },
    "Glow": [
      {
        "_attrs": {
          "alpha": "1",
          "color": "26112",
          "blur": "0",
          "resolution": "1",
          "blurX": "undefined",
          "blurY": "undefined",
          "strength": "undefined"
        }
      }
    ]
  }
]

and final XML result is:

<Actions>
            <Action>
	         	<Glow alpha="1" color="255" blur="0" resolution="1" blurX="undefined" blurY="undefined" strength="undefined"/>
                     <Glow alpha="1" color="255" blur="0" resolution="1" blurX="undefined" blurY="undefined" strength="undefined"/>
                  <Glow alpha="1" color="26112" blur="0" resolution="1" blurX="undefined" blurY="undefined" strength="undefined"/>
           </Action>
</Actions>

as you can see, <Action> has no attrs as it should look like this:

<Action delay="0" duration="2000" easingId="10">
       <Glow alpha="1" color="255" blur="0" resolution="1" blurX="undefined" blurY="undefined" strength="undefined" />
</Action>
 <Action delay="0" duration="2000" easingId="10">
           <Glow alpha="1" color="255" blur="0" resolution="1" blurX="undefined" blurY="undefined" strength="undefined" />
 </Action>
 <Action delay="3" duration="1998" easingId="20">
           <Glow alpha="1" color="26112" blur="0" resolution="1" blurX="undefined" blurY="undefined" strength="undefined" />
</Action>

this is a problem for us as we need to support both arrays and attributes.
tx

Sean

Code simplification

const depth = (config.depth) ? config.depth : 0;

Should be

const depth = config.depth || 0;

Doesn't matter if config.depth is falsy 0, it'll be the same result either way.

Update npm version

Please. Update npm version. With the new contribution of H2SO5: added support for booleans, more unit tests

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.