Not too long ago, I was facing the issue that I needed to be able to make any record so that I could transfer this record – I wanted to be able to find a way to take any record and be able to transfer it to a façade codeunit, to an event subscriber, well, maybe even email the record. I needed to come up with a way that I can use the same functionality, regardless what record it is and I also wanted to be able to display this record easily.
Let’s look at this in more detail: One requirement was to transfer the record to an event subscriber, right? So, what is the natural way to solve this issue? Yes, a RecordRef. This also works well with the next requirement: Being able to display a record easily. If the table definition is done right and it has the proper page definitions, you can easily use a RecordRef in this way:
Another requirement was that I wanted to transfer that to a façade codeunit and decouple functionality so that the actual processing of the transferred data can be changed or additional processing functionality can be added without having to change the “sending part” of the functionality. This could even be combined with discovery events and have the individual data processing codeunits register themselves in the functionality. This means, I could create an event that is raised when data has to be processed and the subscriber can use the RecordRef in the event to process the proper data.
The problem with this is that the processing logic needs to know the event definitions so that it can subscribe to the event. This is not ideal. Another issue I am facing is that I can only process records (granted that was the requirement, but I would like to be able to be even more generic). So, instead of RecordRef, I could use a variant and make it possible to process any type of data, which would bring us to the Variant Façade.
We now are able to process any type of data, even records. But we still have to know the event definition so that we can subscribe to it. I already mentioned that this is not ideal, so we need a way to store the data differently, in a way that I can do a “CODEUNIT.RUN”. Which brings us again to a pattern proposition by Vjeko – described in his blog post TempBlob Façade: A pattern proposition. I can store any type of data in the blob and transfer it to the processing data via “CODEUNIT.RUN”.
Looking back to my requirements – I want to be able to transfer records and possibly even email them somewhere. Obviously, this means that I have to translate the record into something different that I can store in a blob or send in an email (maybe as an attachment or in the body).
How do I fit a record into a blob? That’s the question. Somehow, we need to serialize that record. That means we need to translate the record into something that can be stored in a file, an email body, or even in a blob. Everyone thinks usually of XMLports to easily translate a record into text, since text can be stored in a blob or file or email. There is one issue with an XMLport – you will have to create one for each table you want to specify or code quite a bit to be able to generalize this. But it is possible and I actually went a rout that is similar to that.
I am serializing the record into a Json string. I chose that over XML since the .NET framework has some easy functionality to turn objects into Json strings (or to serialize an object into a string). I first tried to go the direct route to pass the record directly to the Json serializer, but this didn’t work – unless I would have written a custom serializer. This would have required an external DLL and I didn’t want to do that.
Here is what I am doing: I am taking a RecordRef and turn this into a dictionary. And then I am using the standard .NET classes to serialize the dictionary into a Json string. I am also going through the effort to actually store the field and record definition together with the data, so that I later know what to do with the data. This makes it generic enough to even simulate a “TRANSFERFIELDS”. This means, I could serialize a Sales Header into a Json string and deserialize this later into a Purchase Header – as long as the fields have the same type and length.
I have the actual complete code attached to this blog, it contains three codeunits. One to serialize a record, one to deserialize a record, and a helper codeunit. You would use it as follows:
// The RecRef already contains some record
IF Serialize.RecordToJson(RecRef, JsonText) THEN
The main part in the serializing codeunit is shown in this code snippet:
[TryFunction] RecordToJson(RecordVariant : Variant;VAR JSonText : Text)
IF NOT RecordVariant.ISRECORD AND
NOT RecordVariant.ISRECORDID AND
IF RecordVariant.ISRECORD THEN
IF RecordVariant.ISRECORDID THEN
IF RecordVariant.ISRECORDREF THEN
RecRef := RecordVariant;
// add record information to dictionary
// add fields
Basically, I am using the helper codeunit to actually create my dictionary, then load some information about the record into the dictionary. Then create another dictionary that holds all fields and add this. The dictionary that holds the fields also holds the field definition for each field.
To deserialize it, it is even easier:
[TryFunction] JsonToRecord(JSonText : Text;VAR RecRef : RecordRef)
The helper codeunit has different functions to deal with the different data types. It even works with blobs and MediaSets.
Download objects (text version): JsonSerializer