My first post is about GZip compressed API responses
Do you ever find yourself wondering how to decode GZip API responses in Business Central? Yeah, me neither.
Hey, welcome to my blog! I've been thinking of opening one for a while, I have a lot of topics I want to talk about, but I could never decide which was supposed to be "the one".
I got stuck thinking that the first post needs to be magical, it needs to impress people, it must depict the author as the best technical writer out there... or something. But then, a colleague came to me for help with his API integration and we couldn't find a straightforward answer on the search engines, so here it goes. This is now that answer (at least I hope so).
My colleague was tasked to integrate BC with some REST thing. Easy, even without LLMs to help (but girl, do LLMs shine when it comes to these integrations). Only problem: the API seemed to only respond with gibberish. Weird characters with absolutely no sense. We got ourselves an encoding issue!
Content-Type: application/json;charset=UTF-8
Content-Encoding: gzipSo how does one deal with compressed responses? Of course, with the Data Compression codeunit in the System Application. This neat little object provides two methods that will help you solve this particular issue: IsGZip and GZipDecompress.
Let's look at the actual methods in the Data Compression Impl. codeunit in case we're having trust issues:
procedure IsGZip(InputInStream: InStream): Boolean
var
OriginalStream: DotNet Stream;
ID: array[2] of Byte;
begin
if (InputInStream.Length < 2) then exit(false);
OriginalStream := InputInStream;
InputInStream.Read(ID[1]);
InputInStream.Read(ID[2]);
// from GZIP file format specification version 4.3
// Member header and trailer
// ID1 (IDentification 1)
// ID2 (IDentification 2)
// These have the fixed values ID1 = 31 (0x1f, \037), ID2 = 139 (0x8b, \213), to identify the file as being in gzip format.
OriginalStream.Position := 0;
InputInStream := OriginalStream;
exit((ID[1] = 31) and (ID[2] = 139));
end;See how IsGZip reads those magic bytes and then resets the position of the stream for us to read later? Quite neat.
procedure GZipDecompress(InputInStream: InStream; DecompressedOutStream: OutStream)
var
DotNetCompressionMode: DotNet CompressionMode;
begin
GZipStream := GZipStream.GZipStream(InputInStream, DotNetCompressionMode::Decompress);
GZipStream.CopyTo(DecompressedOutStream);
GZipStream.Dispose();
end;Dear old DotNet comes to the rescue! We can use it because we are accessing the Impl codeunit from its public interface. Trust is restored and we can safely call these methods to calm our headache.
local procedure MyDecompressionProcedure(Response: HttpResponseMessage; var ResponseText: Text)
var
DataCompression: Codeunit "Data Compression";
TempBlob: Codeunit "Temp Blob";
ResponseInStream, DecompressedInStream : InStream;
DecompressedOutStream: OutStream;
begin
Response.Content.ReadAs(ResponseInStream);
if DataCompression.IsGZip(ResponseInStream) then begin
TempBlob.CreateOutStream(DecompressedOutStream);
DataCompression.GZipDecompress(ResponseInStream, DecompressedOutStream);
TempBlob.CreateInStream(DecompressedInStream);
DecompressedInStream.ReadText(ResponseText);
end;
end;So this is it, it may not be much but it's honest work I'm hoping this can help someone (maybe me as soon as I forget about this). See you next time!