Skip to content

XMLMarshaller

do- edited this page Dec 16, 2022 · 35 revisions

XMLMarshaller is a companion class to XMLSchemata implementing serialization of data objects to XML string according to a given XML schema element (normally, a complex type).

Instance methods

stringify

const xs = await XMLSchemata.fromFile ('wsdl_related_stuff.xsd')

const m = xs.createMarshaller (localName, namespaceURI)

const xml = m.stringify (data /*, {
// localName   : 'otherThanDataRootKey', 
// declaration : {encoding: 'UTF-5'},
}*/)

XMLMarshaller can be considered a JSON to XML converter: it does the almost exactly opposite to XMLNode.toObject, with similar (but inverse) structure transformations.

Scalar type conversions

  • boolean values are always written as false or true; a mapping is predefined for strings '0'/'1', 'false'/'true', 'N'/'Y', other values are subject to standard toBoolean;
  • if a Number or a BigInt is supplied for a date or dateTime field, it's used as an argument for the Date constructor (number of milliseconds since 1970-01-01);
  • for a date field, any string of length 10 is passed through unchecked (it's expected to de in YYYY-MM-DD format, be careful), otherwise the Date object is constructed and (re)serialized;
  • for integer and all its descendants, a BigInt or (for int and shorter types) a Number is created, checked and then serialized;
  • for float and double, a Number is created with parseFloat, then serialized;
  • for decimal too, parseFloat is used, but then, if fractionDigits restriction is set, the string is formed with toFixed;
  • QNames are expected to be supplied as {localName, namespaceURI} objects, for which qualified names are generated according to the current namespace map;
  • all other values are converted toString, special characters are escaped.

Text content vs. attributes

Attribute only elements are naturally mapped from scalar valued plain objects:

{Order: {amount: 1, price: 2.34}} // <Order amount="1" price="2.34"/>

Text only child elements are normally represented by scalar properties of data objects:

{Outer: {Inner: 123}} // <Outer><Inner>123</Inner></Outer>

But how to represent data for elements with both attributes and text content? In this case, the text must be presented as a null named pseudo attribute:

{Request: {Id: 1, null: 'HELO'}} // <Request Id="1">HELO</Request>

Dealing with wildcards

Some schemata (like SOAP 1.1) contain any and anyAttribute wildcards corresponding to XML fragments of unknown structure.

For stringify to fill in such a wildcard, the content must be presented as a special object with null key is provided:

xs.stringify ({
  Envelope: {
    Body: {                                // contains xs:any, xs:anyAttribute
      null: { // cannot be localName, so no possible confilct here
        '<someRequest>CODE</someRequest>': // raw XML content to be injected as xs:any
        {Id: 1 /*, ...*/}                  // "any" attributes key/value pairs
      }},
    }
  }
)

yields

<ns0:Envelope xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/">
  <ns0:Body Id="1"><someRequest>CODE</someRequest></ns0:Body>
</ns0:Envelope>

In application code, such values may be produced with the XMLSchemata.any convenience method.

Limitations

  • Probably forever:
    • Virtually no validation of any kind
      • except for edge cases like a true value for a dateTime field: then, XMLMarshaller will throw an error
      • and, maybe, later, for exotic scalar types like gDay)
    • No xsi:type polymorphism
  • Minor performance issues not to be fixed shortly:
    • Elements with zero length content have explicit closing tags, self enclosing is not used.
    • " are escaped as &quot; not only in attribute values, but in text nodes too.
  • Pesky things
    • choice is implemented as an alias to sequence: if data are supplied for two or more branches of one choice, the result will be invalid.
    • hexBinary and base64Binary encoding should be supported for Buffer values.

Static methods

declaration

Generates the XML declaration pseudo-tag.

const decl = XMLMarshaller.declaration ({
  version: 3.14159,  // '1.0' by default
  encoding: 'UTF-6', // no default; may be missing
  standalone: 'no',  // no default; may be missing
})