In my current project I’ve created a Web Service-consumer in NAV that maps payload-data flexible using the Data Exchange Definition. Unfortunately in REST- and SOAP-payloads, binary information (JPG, PDF etc.) is delivered as a string. So, I’ve got a little task to find a way to differ between a string (like an address line) and binary information.
As all REST- and SOAP-Web Services, the payload of the Web Service I’m using, delivers the payload as a structured, but simple text-file. This means, all data is delivered in Text-format. But to be able, for example, to store price-information in the database and use it in mathematical calculations, it is necessary to transform the value from Text to Decimal. My Web Service-consumer is able to read the payload, to identify and to transform the DataTypes automatically. But as soon as binary information will be delivered in the payload, my consumer reaches a limit because the binary information would be stored as Text.
Luckily, all binary information of a REST- or SOAP-service will be encoded in Base64-format, so it is an easy task to identify whether it’s a regular text-value (like a company name or delivery instructions) or binary-information.
Encoded data will always have the following characteristic:
- The length of a Base64-encoded string is always a multiple of 4
- Only these characters are used by the encryption: “A” to “Z”, “a” to “z”, “0” to “9”, “+” and “/”
- The end of a string can be padded up to two times using the “=”-character (this character is allowed in the end only)
Edit 23rd April 2021:
If you are interested in C/AL-code, please checkout the code below.
Identification of Base64 in C/AL
Based on this information it was possible to build a function that is able to identify the value of a string as Base64-encoded.
LOCAL PROCEDURE isBase64@1000000002(pVariant@1000000000 : Variant) : Boolean; VAR i@1000000001 : Integer; BEGIN // the length of a base64 is always a multiple of 4 // allowed characters in a base64 are: 'A'..'Z', 'a'..'z', '0'..'9', '+', '/' // the end can be padded with up to 2 '=' // check if the length is a multiple of 4 IF (STRLEN(FORMAT(pVariant,0,9)) / 4 MOD 1) <> 0 THEN EXIT(FALSE); // check each character if allowed FOR i := 1 TO STRLEN(FORMAT(pVariant,0,9)) DO IF NOT (COPYSTR(FORMAT(pVariant,0,9),i,1) IN ['A'..'Z', 'a'..'z', '0'..'9', '+', '/']) THEN BEGIN // if not allowed character found, check if already at end of string (and check for '=') IF (i = STRLEN(FORMAT(pVariant,0,9))-1) OR (i = STRLEN(FORMAT(pVariant,0,9))) THEN BEGIN IF COPYSTR(FORMAT(pVariant,0,9),i,1) <> '=' THEN EXIT(FALSE); END ELSE EXIT(FALSE); END; EXIT(TRUE); END;
Additionally, I’ve created a function that decodes the Base64-string and stores it in a BLOB-field.
[TryFunction] LOCAL PROCEDURE TryToStoreVariantAsBLOB@1000000001(pVariant@1000000000 : Variant;VAR pTempBLOB@1000000001 : TEMPORARY Record 99008535); VAR BlobWriter@1000000004 : OutStream; Convert@1000000003 : DotNet "'mscorlib, Version=18.104.22.168, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Convert"; MemoryStream@1000000002 : DotNet "'mscorlib, Version=22.214.171.124, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.IO.MemoryStream"; BEGIN MemoryStream := MemoryStream.MemoryStream(Convert.FromBase64String(pVariant)); pTempBLOB.Blob.CREATEOUTSTREAM(BlobWriter); MemoryStream.CopyTo(BlobWriter); END;
I have compiled the two functions into a Codeunit (including an example) and published for download on mibuso.com.