This week's build of Elements introduces a new aspect-based feature to Elements RTL: support for simple and easy no-code serialization.

With Elements .2981, you can now easily add the ability for a class to be read from and stored as Json or XML, simply by applying the new [Codable] aspect to it:

type
  [Codable]
  User = public class
  public

    property ID: Guid;
    property Name: String;
    property Email: String;
    property Telephone: String;
    property ZipCode: String;
  
  end;

Once a class is marked as such, you can use the new Enoder and Decoder descendant classes in Elements RTL to convert instances of that class into encoded data or create new instances from encoded data. Coder implementations are provided for Json and XML, but you can, of course, also create your own – for custom data formats, or if you want more flexibility or different output than you are getting from the provided implementations.

var lCoder := new JsonCoder;
lCoder.Encode(lUserA);
writeLn(lCoder.ToJson);
var lCoder := new JsonCoder withJson(lJson)
var lUser := lCoder.Decode<User>;

Controlling the Coding Format

There are a few ways you can influence how your types get encoded (and decoded). This is helpful to match your personal taste, but also if you need to conform to predefined Json or XML schemes that don't exactly match your code style.

By default, the names of the properties are used "as is" to determine the encoded values' name, so ZipCode will come through as e.g. "ZipCode": "44229" in Json.

The [Codable] attribute can take an option parameter of type NamingStyle that lets you override the naming rules. Supported values include camelCase, PascalCase, lowercase and UPPERCASE (sorry for yelling). The default is AsIs.

PascalCase and camelCase use smart logic to convert the identifiers in your code into the proper convention, ez ZipCode would become zipCode in camelCase.

type
  [Codable(NamingStyle.camelCase)]
  User = public class

You can also override the coded name of individual properties by adding the [Encode] attribute with a string value, eg:

[Encode("plz")]
property ZipCode: String;

Encode can also take a boolean parameter instead, and by specifying Encode(false) you can skip the property from being coded. ([Encode(true)] is implied, so specifying that would be redundant ;).

Supported Types

You can encode or decode classes (or records, i guess) that contain any of the following types

  • Integers and UInts (8, 16, 32, 64 bits, plus IntPtr)
  • Float and Double
  • String
  • DateTime (encoded and expected as ISO8601)
  • Guids
  • Json objects and arrays
  • List<T> of supported types
  • arrays of supported types (.NET only)
  • Types that implement IEncodable/ IDecodable or the ICodable combined interface.

The Json coder will encode properties of JsonNode types natively; e.g. if your class has a JsonObject or JsonArray field, it will be encoded as a nested object or array in the result, and read back as such. Other encoders will encode them as string values.

As you may have guessed," Types that ICodable" includes any type you mark as [Codable], since what this aspect does is precisely that: it implements the interface for you, providing strongly-typed code that encodes and decodes all its members (without the need for Reflection).

You can also implement this interface yourself if you want to provide a custom way of how a specific type is encoded or decoded, instead of how [Codable] does it. Maybe you have a type that represents a data blob, and instead of coding its properties, you want to encode its data as a Base64 string.

Convenience

Some additional convenience can be gained in addition to applying [Codable] (or implementing ICodable), you also make your type decent from the Codable helper class. This is entirely optional but gives you additional niceties, such as a constructor withJson(aJson: JsonDocument); constructor, a ToJson, and a default implementation of ToString that returns the type as Json string.

How to use it?

Using RemObjects.Elements.Serialization is easy:

  1. Make sure your project uses (or at least references) Elements RTL (i.e. has a Elements reference).
  2. Add a reference to the new RemObjects.Elements.Serialization.dll from the "Cirrus" tab in the Reference Manager to it.
  3. Add RemObjects.Elements.Serialization to your uses/import clause.

And that's it.

PS: Check out JsonDocument

As part of implementing RemObjects.Elements.Serialization (and working with a lot of Json in general), we have made a ton of improvements and tweaks to the JsonDocument implementation in Elements RTL over the past few years. If you have any need to work with Json data, I recommend you give it a try. (JsonDocument is also fully cross-platform across all Elements platforms).

Feedback

RemObjects.Elements.Serialization is solid and time-proven; I have been using it for internal and Infrastructure projects for going on two years now, and I expect you will find it immediately useful.

Let me know what you think and/or if you have any ideas for improvement!