npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

xml-js-converter

v1.0.0

Published

Convert XML to a JavaScript object and back, following a specific schema. Define your own rules for how to convert attributes and content

Downloads

92

Readme

First, a warning

We use semver, and we're starting at 1.0.0 in order to easily communicate API changes, without breaking semver.

However, it's still beta and should be used with caution.

xml-js-converter

Convert between an XML string and JavaScript objects (in both directions), following a specific schema.

If you only need a generic conversion, you will probably find https://www.npmjs.com/package/xml2js or https://www.npmjs.com/package/xml2json suits your needs better.

What xml-js-converter allows you to do, is specify the way in which certain nodes are converted. For instance, say you had the following XML:

<person id="195">
   <firstName>Alan</firstName>
   <lastName>Turing</lastName>
</person>

What you might want in JavaScript is

{
    person: {
        id: '195',
        firstName: 'Alan',
        lastName: 'Turing'
    }
}

So the id attribute is a property, but so is the sub-element firstName. Converting back should obviously do the same thing, so from the given JavaScript object, you get the same XML back.

Collections / arrays

Say you had the following XML

<person id="195">
   <firstName>Alan</firstName>
   <lastName>Turing</lastName>
   <skills>
      <skill id="CS101">Computer theory</skill>
      <skill id="ENIG1">Code breaking</skill>
   </skills>
</person>

(The skills list is by no means exhaustive!)

What you can obtain with xml-js-converter is the following JavaScript object:

{
    person: {
        id: '195',
        firstName: 'Alan',
        lastName: 'Turing',
        skills: [
            { skill: {
                id: 'CS101',
                name: 'Computer theory'
            },
            { skill: {
                id: 'ENIG1',
                name: 'Code breaking'
            }
        ] 
    }
}

Notice the extra "skill" object - ie. an object with a single property "skill". This is so that you can have an array of sub-elements with different tag names. For instance:

<person id="195">
   <firstName>Alan</firstName>
   <lastName>Turing</lastName>
   <attributes>
      <skill id="CS101">Computer theory</skill>
      <achievement id="ENIG1">Cracked the Enigma code</achievement>
   </attributes>
</person>

And end up with the JavaScript:

{
    person: {
        id: '195',
        firstName: 'Alan',
        lastName: 'Turing',
        attributes: [
            { skill: {
                id: 'CS101',
                name: 'Computer theory'
            },
            { achievement: {
                id: 'ENIG1',
                name: 'Code breaking'
            }
        ] 
    }
}

Usage

npm install xml-js-converter

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

fromXml

fromXml(xmlAsString, specification, callback) 

Where callback is function (err, value) { ... }, err is null unless there was an error, and value is the javascript object. specification is defined in the next section.

xmlJsConverter.fromXml('<demo>text content</demo>', {}, function (err, value) {
    
    // value == { demo: { $content: 'text content' } }

});

toXml

toXml(jsObject, specification, callback) 

Where callback is function (err, value) { ... } , err is null unless there was an error, and value is a string with the XML.

Specification

The specification can be an empty object. Otherwise each property can define how to handle that tag name.

For instance:

{
    test: {}
}

Would be a (useless, ie. "non-effect having") specification for:


<test>some content</test>

This would result in

{
    test: { 
        $content: 'some content'
    }
}

$content is the default name for the content of a tag. You can override this in the spec by setting $content.

$content

Set to a string to name the content property. e.g.

var spec = {
    test: {
        $content: 'name'
    }
};

xmlJsConverter.fromXml('<test>charles babbage</test>', spec, function (err, value) {
    // value == { 
    //       test: {
    //              name: 'charles babbage'
    //       }
    // };
});

Set to false to suppress the content. Note that attributes of the node will then be skipped, as there is nowhere to define them.

e.g.

var spec = {
    test: {
        $content: false
    }
};

xmlJsConverter.fromXml('<test>charles babbage</test>', spec, function (err, value) {
    // value == { 
    //       test: 'charles babbage'
    // };
});

To define $content for deeply nested elements, simply define the path to the elements.

e.g.

var spec = {
    test: {
        name: {
            firstName: { $content: false },
            lastName: { $content: false }
        }
    }
};

xmlJsConverter.fromXml('<test><name>' + 
        '<firstName>charles</firstName>' + 
        '<lastName>babbage</lastName>' + 
        '</name></test>', spec, function (err, value) {
    // value == { 
    //       test: {
    //            name: {
    //                firstName: 'charles',
    //                lastName: 'babbage'
    //            }
    //        }
    // };
});

$arrayElement

Set to true to include the children as an array. Each child node will be an object with a single property with the name of the tag, and then follow the conventions of sub-schema.

Note that the array will be the $content property, unless $content is also specified (either set as a name or to false to discard the attributes).

From the example above:

<person id="195">
   <firstName>Alan</firstName>
   <lastName>Turing</lastName>
   <attributes>
      <skill id="CS101">Computer theory</skill>
      <achievement id="ENIG1">Cracked the Enigma code</achievement>
   </attributes>
</person>

To obtain the following JavaScript:

{
    person: {
        id: '195',
        firstName: 'Alan',
        lastName: 'Turing',
        attributes: [
            { skill: {
                id: 'CS101',
                name: 'Computer theory'
            },
            { achievement: {
                id: 'ENIG1',
                name: 'Code breaking'
            }
        ] 
    }
}

You'd use the specification:

var spec = {
    person: {
        firstName: { $content: false },
        lastName: { $content: false },
        attributes: {
            $arrayElement: true,
            $content: false,
            achievement: { $content: 'name' },
            skill: { $content: 'name' }
        }
    }
};

Note how the $arrayElement is true, and also $content is false for the attributes element. This suppresses the $content property for the array. $content for array elements works exactly the same as normal elements.

The specs for achievement and skill will be used for every achievement and skill element in the parent element. If an unknown element occurs, it will simply use a default specification ({})

Contributing

Bugs, pull requests, comments all very welcome.

The tests are written in mocha, using the excellent unexpected assertion library.

To run the tests, just call

npm test

Versioning

We follow semver. We have started at version 1.0, not symbolise "production ready", but to effectively communicate changes.

Roadmap

Things we'd like soon:

  • Support for numeric types
  • Support to automatically case-switch (e.g. PascalCase in XML and camelCase in JS)

Things we'd like eventually:

  • Support for naming nodes differently in JS and XML

  • Support for dropping the wrapper on arrayElements e.g. from

    <people>
      <person name="monica" />
      <person name="steve" />
    </people>

To be transformed to

  { people: [ { name: 'monica' }, { name: 'steve' } ] }
  • Support for using an attribute as the "content" e.g. from
   <director>
      <person name="tim cook" />
   </director>

To be transformed to

{
    director: {
        person: 'tim cook'
    }
}

This would also enable the people array in the previous example to be transformed to a simple array of strings.