What is the serialization repo for?

I noticed that you have a serialization repository on GitHub but there isn’t any documentation for it. It is currently targeting .NET Standard. Will this eventually be able to be compiled for other platforms like Island or is it dependent on .NET classes?

It’s a work in progress, but almost ready for testing and feedback, so if you want to have a go at it, that’d be cool. I was planning to write a blog post about it soon.

Right now it;'s (tested on) .NET only, but the goal is for it to eventually work on all platforms. probably;y, once its ready, I’ll move the core interfaces and the coder classes into Elements RTL, and the library will just keep the aspect.

the included test project should show you well ow to use it.

Essentially a class can be made encodable/decodable by either manually implementing IEncodable/IDecodable (or the ICodable, which combines the two) or (the more common case) by applying the [Encodable]/[Decodable] or just [Codable] aspect to it and letting it do all the heavy lifting of implementing the interfaces for you.

the Codable aspect(s) also takes a NamingStyle that lets yu auto-convert the name to upper, lower, camel or pascal case.

Once a class (f being an instance in this case) is codable, you can have it coded to Json or Xml via

    var j := new JsonCoder;
    j.Encode(f);
    var x := new XmlCoder;
    x.Encode(f);

and decode it back via

    var j := new JsonCoder withJson(...); // or withFile(...)
    var f := j.Decode<Foo>;

    var x := new XmlCoder withXml(...); // or withFile(...)
    var f := x.Decode<Foo>;

Automatic encoding current supports

  • all simple types (large UInt64s are capped to Int64 limits due to limits in the Json class, working on a solution to that
  • nested objects that also are codable
  • arrays of the above

On the list/todo is

  • support for List<T> in addition to arrays
  • testing/support for the other platforms
  • more properties to manually override naming and which propetoes get encoded and which don’t

A real-life example that already works/is used in production:

  [Codable(NamingStyle.camelCase)]
  Location = public class(JsonEntity)
  public
    property ID: Guid;
    property Name: String;
    property MainPictureUrl: String;
    property Address: String;
    property City: String;
    property Coordinates: Coordinates;
    property Fields: array of Field;
  end;

  [Codable(NamingStyle.camelCase)]
  Coordinates = public class
  public
    property Latitude: Double;
    property Longitude: Double;
  end;

  [Codable(NamingStyle.camelCase)]
  Field = public class
    property ID: Guid;
    property Name: String;
    property Size: String;
    property Description: String;
    property PictureUrl: String;
    property Coordinates: Coordinates;
  end;
{
  "name": "...",
  "mainPictureUrl": "...",
  "address": "...",
  "city": "...",
  "coordinates": {
    "latitude": 40.0,
    "longitude" 2.0,
  },
  "id": "9F0EA654-DAB1-4628-9AEE-599DE1D5E8A7",
  "fields": [ {
      "name": "...",
      "id": "2AD99470-ED8A-4B73-8659-51873F73E4EA",
      "size": "full",
      "description": "Test",
      "pictureUrl": null,
      "coordinates": {
        "latitude": 40.0,
        "longitude": 0.2
      }
    }
  ]
}
2 Likes

That sounds really useful, look forward to it.

I think it must be using a newer version of Elements RTL than I have as I get error messages like No static member TryToIntPtr, TryToUIntPtr, or TryToUInt64.

You will need the very latest Elements RTL, yes

OK

I saw there were updates to both repos (Elements and Serialization) since I downloaded so I’ve update them.

When I try to compile Elements.RTL.Echoes.Standard (assuming that’s the one I need) the message below appears:

(E90) Could not load public key

How do I fix that?

You’ll need to create and register a signing key via the sn.exe tool. iirc the syntax is

sn -k dummy.key // to create a key
sn -i dummy.key RemObjectsSoftware // to then register that key; I might have the order reversed here

That worked

I modified the references in Serialization and TestApp to point to the newly compiled Echoes.dll and Elements.dll that from the RTL2-master\source\bin\Debug\NETStandard folder.

Everything compiles without error.

When I attempt to run the TestApp I get “The magic happens here.” followed by the message below.

Common Language Runtime detected an invalid program.
This exception was originally thrown at this call stack:
    RemObjects.Elements.Serialization.Coder.Encode<T>(T) in Coder.Encode.pas
    TestApp.Program.Main(string[]) in Program.pas

If I step through the program.cs then I can see that this occurs on line 28 which is j.Encode(f);

known compiler issue that just got fixed, b ut grab the latest as workaround.

I updated Elements to Version 11.0.0.2859 and downloaded the repositories.

This now compiles and runs up to line 48 without error. So I get a log of one json object and a log of one xml object. The decoder fails with a message unkown type TestApp.Bar2 which I attribute to this not being a complete product yet.

I like the fact that the xml output includes the class names. Is there a reason that the json model does not? It would be nice to be able to decode (deserialize) an object without knowing the type. The decoder might read the class name from the encoded xml and have a return of type object. Internally the decoder reads the class type from the encoded xml and creates an instance of the appropriate class.

Pardon my use of .NET terminology but it’s what I’m most familiar with.

var AnObject = x.Decode();
object AnObject = x.Decode();

You could still have the generic type method x.Decode()

It looks like you’re going to have an attribute that allows you to specify the name of the node in the encoded document and a boolean to exclude the member property.

I am guessing that this will only encode / decode public members (properties). If a private or protected member is decorated with an encode attribute can that be serialized / deserialized as well?

And will Date/Time properties include the time zone information? If yes then please consider including some means of excluding the time zone. I ran into this issue with one of our products. We hosted a customer’s database on our server as a service and they were in a different time zone. When we deserialized the object (specifically a DataTable) .NET saw the time as being from a different time zone. All of the transaction times were automatically adjusted from Central time to Eastern. Naturally the customer called in and asked why all of their times were an hour off. To get around this I removed the time zone details from the date/time column (DataColumn) before serializing the DataTable.

Some means of providing a type converter or custom formatter for a member would be handy. That way if the serializer doesn’t support a member’s data type by default the user could provide a formatter. I’m thinking of things like binary objects (images, etc.)

A Binary serialize / deserialize would also be very beneficial as it would potentially produce a smaller output. I tend to serialize objects (DataTable), send them over a TCP connection, then deserialize them on the other end. There’s a 4 MB limit on the Remoting SDK as I recall. Sometimes I hit that limit even with a binary stream and have had to make accommodations for it.

I get the same with latest, yes. I’ll need to check.

Leep I mind that this is a moving target, I’m making many changes daily, and I made some yesterday that I did test test.

In this case, it looks like is accidentally committed a local hack to get the library to compile for Island (which didn’t have FullName at the time; it does now).:

  result := fTypesCache.FirstOrDefault(t -> t./*Full*/Name = aName);

change this back to Fullname and it should be fine

It does, too.

    {
      "__Type": "TestApp.Bar",

I do wanna look at omitting this if its there base type of the known property, and only emit it when subclasses are involved. for cleaner code in that cases

That should work. I’ll add an overload.

Good Q. right ow I’m focused on Elements RTL’s DateTime, which is always UTC. But the serializier also handles .NET’s System.DateTime which is, let’s say, more weird. It does not contain a timezone, but has a flag for :"UTC, Loc al or “unknown”. I’ll need to think about this.

Right now it always encodes as ISO8601, but one could add an attribute to allow overriding this, later.

That said, i really recommend always treating all datetimes as UTC, and only converting for display.

Good idea, yeah.

You wanna implement one? :wink: JUst add a third Coder subclass to it.

FWIW you can change that, but 4MB probably is a good limit for individual ROSDK calls, yes :wink:

Works like a champ with that change.

1 Like

Do you think it will be much work to get it working on Toffee ? I created a target for Toffee.ios and there are errors like

Cannot get typeof() for value types in Cocoa and type mismatch. The aspect has to be only echoes doesnt it ?

Cheers,
John

Probably, because

generics are super limited at runtime on Toffee (and Java) :frowning:

I’ld need to find an entirely different approach for handling the cases that don’t compile now. Ideas appreciated.

Yes. Once it all works, the runtime classes would be split off into a multi-platform lib (or added to RTL2 itself), and only the aspect would remain as a separate .NET Standard/Cirrus reference.

Yes I see. An Island Darwin target builds successfully.

Yeah, I didn’t test Island yet, but it all compiles, except for support for lists, which (right now) needs virtual generics, which we don’t (and, I’m told, can’t, ever) support, and I’ve IFDEF’ed out. So I’ll need to see if I can rework this to use a different system.

Are generics really required for the serialization library?

The decode could just have an Object type return then cast it to the appropriate type.

var x := new XmlCoder();

AnObject Item = (AnObject)x.Decode();

I’ll have to revisit this to see if I can make it less dependent on generics, for the othen platforms, yes. But it’s not as simple as it sounds, as I’ve gone back and forth thru various ways to do this Types, using typeOf, etc, and (for now) this current one is the system that worked best.

For example, even the currently entirely generic-less Encode method doesn’t work for Toffee:

    method Encode(aName: String; aObject: Object); private;
    begin
      if assigned(aObject) then begin
        case aObject type of
...
          Int8: EncodeInt8(aName, aObject as Int8); // E607 Cannot get typeOf() for value types on Cocoa
          Int16: EncodeInt16(aName, aObject as Int16); // E607 Cannot get typeOf() for value types on Cocoa
          Int32: EncodeInt32(aName, aObject as Int32); // E607 Cannot get typeOf() for value types on Cocoa
...

When I split the library into 2, I moved the aspect into a dotnet standard library and kept the rest in the serialization library.

The .net console app works fine. In the island console when i build I get errors like

E:                   Unknown identifier "NamingStyle" [/Users/JohnMoshakis/Documents/develop/Serialization/Island.TestApp/Program.pas (64)]
E:                   Unknown type "IEncodable" [/Users/JohnMoshakis/Documents/develop/Serialization/Island.TestApp/Program.pas (98)]
E:                   Unknown type "IDecodable" [/Users/JohnMoshakis/Documents/develop/Serialization/Island.TestApp/Program.pas (98)]
E:                   Exception while applying aspect "RemObjects.Elements.Serialization.Codable": Unknown identifier "RemObjects.Elements.Serialization.IEncodable" [/Users/JohnMoshakis/Documents/develop/Serialization/Island.TestApp/Program.pas (97)]
E:                   Exception while applying aspect "RemObjects.Elements.Serialization.Codable": Unknown identifier "RemObjects.Elements.Serialization.IEncodable" [/Users/JohnMoshakis/Documents/develop/Serialization/Island.TestApp/Program.pas (141)]
E:                   Unknown identifier "JsonCoder" [/Users/JohnMoshakis/Documents/develop/Serialization/Island.TestApp/Program.pas (29)]
E:                   Unknown identifier "XmlCoder" [/Users/JohnMoshakis/Documents/develop/Serialization/Island.TestApp/Program.pas (34)]
E:                   Unknown identifier "JsonCoder" [/Users/JohnMoshakis/Documents/develop/Serialization/Island.TestApp/Program.pas (51)]

In the island console, using code completion the only thing I can is ICodable. Any ideas what I might of done wrong ?

Cheers,
John

Good Q re:NamingStyle. i might have to check with @ck on this. In theory, this type would only be needed inside the Aspect and not on Island level, as its only used in the aspect declaration. But this might be a bit of a gray area, because technically your Island code uses an enum type that’s not defined on Island level (only in .NET). What happens if you duplicte the type (with an ISLAND $IFDEF) in the main library?

As for the other areas, it sound like your project simply doesn’t reference the Island lib that defines these?

Can I see the entire solution has you have it?