Teaser : kbmMW’s new object notation

kbmMW now supports XML, JSON, BSON, Messagepack and YAML natively, and includes a new common Object Notation format for easy conversion.

JSON and XML has been supported natively by kbmMW for years. Later kbmMW also supported marshalling of native Delphi objects to and from JSON and XML.

In addition to it being a very nice way to stream Delphi objects, the feature also allowed for a way to convert XML to JSON and visa versa.

However, the upcoming version of kbmMW now includes a new object notation framework, that makes it easy to convert from one notation to another, without going through a native Delphi object.

The new version of kbmMW not only supports JSON and XML, but now also YAML (which is a more human readable format) and the compact binary Messagepack and BSON formats, natively, both for creating your own object notation trees in code, and for marshalling Delphi objects to and from.

With the many new object notation formats supported by kbmMW, it also made sense to find a way to make it easy to convert between them, without having to marshal to and from native Delphi objects.

For that reason the kbmMW Object Notation format was invented. The object notation format is based on a number of classes:

  • TkbmMWONCustomObject (from which the classes listed below descends)
  • TkbmMWONObject (for key/value type data)
  • TkbmMWONArray (for array type data)
  • TkbmMWONNative (for string, integer, floats, date/time, binary values, custom native values and more)

In addition a number of abstract stream classes was developed, which purpose is to handle converting the object notation trees to and from various string or binary formats, and a set of abstract classes that handles marshalling object notation trees to and from native Delphi objects.

JSON, YAML, BSON and Messagepack object notation classes directly descends from the Object Notation classes.

For JSON:

  • TkbmMWJSONObject
  • TkbmMWJSONArray
  • TkbmMWJSONNative
For YAML:

  • TkbmMWYAMLObject
  • TkbmMWYAMLArray
  • TkbmMWYAMLNative
For BSON:

  • TkbmMWBSONObject
  • TkbmMWBSONArray
  • TkbmMWBSONNative
For Messagepack:

  • TkbmMWMessagepackObject
  • TkbmMWMessagepackArray
  • TkbmMWMessagepackNative

Since XML is a more rich notation format, it was decided to leave its SAX based parser (which the XML DOM class use) and instead add support for converting the XML DOM tree to and from the Object Notation format. This way its easy to convert between different notation formats, without sacrificing the more advanced features of our native XML parser.

The above specialized object notation classes, are in reality very very slim.
Thus the underlying TkbmMWON…. classes are sufficient for conversion. So even if one build a JSON object notation tree in code, it can still be understood by the Messagepack formatter code etc.

Generally it’s recommended using the non specialized classes instead of the specialized ones for simplicity reasons.

So lets try to build a simple object notation tree:

var
 father,child:TkbmMWONObject;
 children:TkbmMWONArray;
begin
 father:=TkbmMWONObject.Create;
 father.AsString['name']:='Joe Simpson';
 father.AsInt32['age']:=42;
 children:=TkbmMWONArray.Create;
 child:=TkbmMWONObject.Create;
 child.AsString['name']:='Joe Simpson Jr';
 child.AsInt32['age']:=12;
 children.Add(child);
 father.AsArray['children']:=children;
 ...

This basically makes a father object with name and age, and a list of children (containing only one child with name and age). The object notation tree should now look like this:

2017-02-01-01_09_51-xbox

So lets convert this to JSON:

var
 json:TkbmMWJSONStreamer;
 s:string;
begin
 json:=TkbmMWJSONStreamer.Create;
 try
  // If set to true, an exception will be raised in case a type is not supported
  // by the specific streamformat. An event also exists which one can hook
  // into, to get information about what type fails etc.
  json.ExceptOnUnknownType:=true;
  s:=json.SaveToUTF16String(father);

  // s now contains the serialized object in JSON format.
 finally
  json.Free;
 end;
end;

Resulting in s containing:

{"name":"Joe Simpson","age":42,"children":[{"name":"Joe Simpson Jr","age":12}]}

or to YAML:

var
 yaml:TkbmMWYAMLStreamer;
 s:string;
begin
 yaml:=TkbmMWYAMLStreamer.Create;
 try
  // If set to true, an exception will be raised in case a type is not supported
  // by the specific streamformat. An event also exists which one can hook
  // into, to get information about what type fails etc.
  yaml.ExceptOnUnknownType:=true;
  s:=yaml.SaveToUTF16String(father); 

  // s now contains the serialized object in YAML format.
 finally
  yaml.Free;
 end;
end;

Resulting in s containing (with linefeeds):

name: Joe Simpson
age: 42
children:
  -
    name: Joe Simpson Jr
    age: 12

BSON and Messagepack follows the same pattern as JSON and YAML.

And for XML:

var
 xml:TkbmMWDOMXML;
 s:string;
begin
 xml:=TkbmMWDOMXML.Create;
 try
  xml.LoadFromObjectNotation(father);
  xml.Top.Name:='father';
  xml.AutoIndent:=true;
  xml.AutoLineFeed:=true;
  xml.Typed:=false;
  s:=xml.SaveToString;

  // s now contains the serialized object in XML format.
 finally
  xml.Free;
 end;
end;

Resulting in s containing (with linefeeds):

<father>
 <name>Joe Simpson</name>
 <age>42</age>
 <children i="0">
  <name>Joe Simpson Jr</name>
  <age>12</age>
 </children>
</father>

In BSON, the output would be a binary value (a stream or a byte array). A Base64 encoded version of the above object notation tree would be:

XQAAAAJuYW1lAAwAAABKb2UgU2ltcHNvbgAQYWdlACoAAAAEY2hpbGRyZW4ALwAAAAMwACcAAAACbmFtZQAPAAAASm9lIFNpbXBzb24gSnIAEGFnZQAMAAAAAAAA

and a Base64 encoded Messagepack stream would look like:

g6RuYW1lq0pvZSBTaW1wc29uo2FnZSqoY2hpbGRyZW6RgqRuYW1lrkpvZSBTaW1wc29uIEpyo2FnZQw=

The Object Notation classes also supports letting the developer indicate if a property key/value pair of type TkbmMWONNative should be streamed as an attribute or an element in XML.

Further the TkbmMWONNative class supports custom values, like the BSON Decimal128 value. As no other streamers understand that value type, using custom types do limit lossless conversion between different object notation formats.

Delphi native objects can be marshalled to and from the Object Notation format, and thus also to and from the above supported specialized object notation formats formats.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s