Skip to content

Api Reference (1.0)

Jean-Philippe Gravel edited this page Jun 5, 2015 · 3 revisions

Table of content

serializer

constructor([behavior])

The constructor creates a serializer instance using the optional behavior object.

  • behavior
    • arrays
    • attributes
    • cdatas
    • texts

See the serializer.behavior section for details.

serializer.behavior

Tells serialize and deserialize methods how to behave with fields or XML elements. The behaviorobject contains two fields:

arrays

The array mappers relate a field path with its child item name. The key must be the complete path to the corresponding field name, including all parents up to the XML root element name separated by dots(.) (that was the field path definition). The value part is the name of the array item.

A behavior object example with two defined array fields:

var Serializer = require("damn-simple-xml");
var serializer = new Serializer({
  arrays: {
    "organization.employees": "employee",
    "organization.customers": "customer"
  }
});

When deserializing XML and encountering the employees element inside the root element named organisation, a JavaScript array will be created and each child elements will be added to that array (regardless of the element and array item name). When serializing, each children of the employees will be serialized inside an employee XML element. The same goes for customers.

Any Depth Declarator

(since 1.2.0)

It is also possible to declare an element name to be deserialized as an array at any depth within the XML path. To do that use the "*." declaration prior to the element name you want to define as an array. For example:

var serializer = new Serializer({
    arrays: {
        "*.children": "item"
    }
});

The aforementioned declaration will serialize all elements with "item" as name within an array named "children" anywhere within the object graph. The same goes for deserialization.

attributes

The attribute mappers relate a field path and an array of field names to be serialized as attributes within the parent element. An attribute can be of any basic type and of the Date type. Objects and arrays are prohibited and an error object will be returned in the callback function if such a field would be declared as an attribute. The attribute behavior have no effect on deserialization.

A behavior object example with attributes field definitions:

var Serializer = require("damn-simple-xml");
var serializer = new Serializer({
  arrays: {
    "organization.employees": "employee",
    "organization.employees.employee.emails": "email"
  }
  attributes: {
    "organization.employees.employee.emails.email": ["type"]
  }
});

When deserializing and encountering the field type inside the email field of the organization object hierarchy, the attribute type will be added to the email element with its corresponding value between double quotes(").

Any Depth Declarator

Not yet supported

cdatas

(since 1.1.2)

When serializing, fields marked as "cdata" will be rendered with the surrounding "" markers. The "cdatas" field of the behavior object are declared as a path to an object field and an array of fields that should be serialized as CDATA. For example:

var serialized = new Serializer({
  cdatas: {
    "employees.employee": ["notes", "remarks"]
  }
});

In the previous example, fields "notes" and "remarks" from the "employee" object will both be serialized as CDATA.

Any Depth Declarator

Not yet supported

texts

The text mappers relate field path to the field inside of it that will be dumped directly as text without enclosing tags. Object and array fields are prohibited and an Error object will be sent to the callback function if such fields where to be declared as text. Any non-string fields will be stringified. Date type will be ISOStringified (I mean... yeah... you know).

A behavior object with texts field definition

var Serializer = require("damn-simple-xml");
var serializer = new Serializer({
  arrays: {
    "organization.employees": "employee",
  }
  attributes: {
    "organization.employees.employee": "notes"
  }
});

When serializing using the previous serializer, the content of the notes field from the employee field will be dumped directly as text within the employee node. When deserializing, any text directly encountered within the employee element will be concatenated in the notes field, separated by spaces.

Any Depth Declarator

Not yet supported

serializer.deserialize(xml, callback)

Creates a JavaScript object based on the xml parameter.

parameters

  • xml: The XML input data to be deserialized into an object. This parameter can be either a string or a stream.Readable instance.
  • callback: a function(err, root) called when the deserialization is complete. If an error occurs, the err parameter will contain the an Error object detailing the issue. If successful, the err parameter will be null and the deserialization result will be in the root object. See root object documentation.

behavior

Deserialization will consider all attributes and sub nodes of the current element as fields of the currently deserialized object. Each values are parsed and automatically cast to the type they are the most likely to be.

In the absence of a corresponding behavior.arrays field path definition, when an XML element is encountered and there is already a corresponding field in the deserialized object, the parent field is changed from an object to an array containing both XML elements. Each other child element will then be deserialized as an array item of the current field, regardless of its name. This behavior is called automatic array discovery. This example should make that point clear:

XML string:

<employee id="123">
  <firstName>John</firstName>
  <lastName>Doe</lastName>
  <dateOfBirth>1984-03-12T00:00:00.000Z</dateOfBirth>
  <languages>
    <language>Java</language>
    <language>C++</language>
    <language>C#</language>
    <language>JavaScript</language>
  </languages>
</employee>

root.data:

{
  id: 123,
  firstName: "John",
  lastName: "Doe",
  dateOfBirth: "1984-03-27T00:00:00.000Z",
  languages: ["Java", "C++", "C#", "JavaScript"]
}

Given the absence of a second element with the same name, the following deserialized object will occurs:

<employee id="123">
  <firstName>John</firstName>
  <lastName>Doe</lastName>
  <dateOfBirth>1984-03-12T00:00:00.000Z</dateOfBirth>
  <languages>
    <language>Java</language>
  </languages>
</employee>

root.data:

{
  id: 123,
  firstName: "John",
  lastName: "Doe",
  dateOfBirth: "1984-03-27T00:00:00.000Z",
  languages: {
    language: "Java"  
  }
}

To prevent this, all arrays should be declared beforehand with the following behavior.arrays:

var Serializer = require("damn-simple-xml");
var serializer = new Serializer({
  arrays: {
    "employee.languages": "language"
  }
})

The deserialize function needs that the arrays object returns a "truish" value (non-null and non-empty string) when checking for behavior.arrays[fieldPath]. The actual array item name part is used only for serialization purpose.

So given the behavior in the previous example, the resulting employee.languages field will turn back to the expected array:

{
  id: 123,
  firstName: "John",
  lastName: "Doe",
  dateOfBirth: "1984-03-27T00:00:00.000Z",
  languages: ["Java"]
}

When in-line text is encountered within a set of XML element or inside an element with at least one attribute, a field named _text will be automatically created for the current object with the trimmed text. Each in-line text met thereafter will be trimmed and concatenated with a space to the _text field. If a texts field path is mapped to a field name, instead of using the default _text field, the specified field name will be used.

<employee>
  This is
  <firstName>John</firstName>
  some text
  <lastName>Doe</lastName>
  interleaved with
  <email type="personal">[email protected]</email>
  elements
</employee>

Will result in:

{
  _text: "This is some text interleaved with elements",
  name: "employee",
  data: {
    firstName: "John",
    lastName: "Doe",
    email: {
      type: "personal",
      _text: "[email protected]"
    }
  }

serializer.serialize(root, callback)

Generates XML given the root name and data.

parameters

  • root
    • name: The name of the root element where the data object will be serialized.
    • data: The object to be serialized.
  • callback: a function(err, xmlpart, level) called during serialization each time some XML text is available. If an error occurs, the err parameter will contain the an Error object detailing the issue. When invoked during serialization, the level parameter value will be above 0. Once the level reaches 0, it means that the serialization is over.

behavior

The serialization dumps every elements one after the other in one long line. This is a design choice. We consider XML content beatification out of scope of the current library. To make the resulting XML human readable (quite easier to debug), just copy-paste the resulting XML data inside Notepad++ and format it using the XML plug-in. If you are working on some other OS than Windows (as I do most of the time) there is a plethora of XML formatter around the Web for your convenience.

By default, if no behavior object is specified at construction, the root.data object will be serialized in a root.name element. After that, each fields of the root.data object will be recursively serialized in an element of the same name. When _text element is encountered, it is dumped in the parent element without enclosing XML tags.

When serializing, if there is no array field definition corresponding to the current array field name in the serializer behavior object, array items will be named according to the parent item name concatenated with "Item". For example:

serializer.serialize({
  name: "employees",
  data: [
    {
      id: 123,
      department: "Marketting",
      fullname: "John Doe"
    },
    {
      id: 456,
      department: "Administration",
      fullname: "Jane Doe"
    }
  ]
});

will result in (XML data is formatted for the example purpose):

<employees>
  <employeesItem>
    <id>123</id>
    <department>Marketting</department>
    <fullname>John Doe</fullname>
  </employeesItem>
  <employeesItem>
    <id>456</id>
    <department>Administration</department>
    <fullname>Jane Doe</fullname>
  </employeesItem>
</employees>

The following example shows how to control both array names and attributes with the following behavior object given to the constructor:

var Serializer = require("damn-simple-xml");
var serializer = new Serializer({
  arrays: {
    "employees" : "employee"
  },
  attributes: {
    "employees.employee": ["id", "department"]
  }
});

will result in:

<employees>
  <employee id="123" department="Marketting">
    <fullname>John Doe</fullname>
  </employee>
  <employee id="456" department="Administration">
    <fullname>Jane Doe</fullname>
  </employee>
</employees>

It is important to note that the serialization callback is called repetitively as soon as a XML string chunks is available (xmlpart paramter). To construct the whole XML document, simply concatenate all chunks in one master variable. Dumping to a stream would be a great option as well. When done, the callback will be invoked with the level parameter value at 0. It indicates that the serialization is over for the current serialization function call.

Serialization callback example

var xml = "";
serializer.serialize({
  name: "employees",
  data: employeeList
}, function(err, xmlpart, level) {
  if (err) {
    console.log(err);
    return;
  }
  xml += xmlpart;
  if (level === 0) {
    // We are done serializing! Lets see what our XML look like.
    console.log(xml);
  }
});