Stream Binary to JSON work around. HealthShare 2017.2
Hi Dev Community,
My team is currently working on a project where we need to post pdf documents to a 3rd party REST API.
The API specifies a field in the JSON request message to contain the 'physical content' of the file as an array of bytes, example of the JSON request as follows - data truncated for readability purposes:
{
"CaptureSource":2,
"RecipientID":"ID34",
"Document": {
"Guid":"5D847A4E9CC1485382DC4A5F5DC80D6C",
"EventDate":"2019-01-14T13:23:46",
"FileExtension":"pdf",
"FileContent":"JVBERi0xLjQNCiXi48/TDQoxIDAgb2JqDQogIDw8DQogICA......"
},
"ActionRequired":false,
}
COB projects array of bytes as %Stream.GlobalBinary stream type. We are able to see the physical contents of the files within our production when we read the files and they travel through the business hosts in our production, but when we call the utility method:
Set tSC=##class(Ens.Util.JSON).ObjectToJSONStream(pRequest,.tJsonPayload,"iuw")
The output blanks out the FileContent element of the JSON message - pRequest being a custom request object with a Property FileContent As %Stream.GlobalBinary , and crashes the API call:
{
"CaptureSource":2,
"RecipientID":"ID34",
"Document": {
"Guid":"5D847A4E9CC1485382DC4A5F5DC80D6C",
"EventDate":"2019-01-14T13:23:46",
"FileExtension":"pdf",
"FileContent":null
},
I've tried creating a %DynamicObject instead and calling %To.JSON(), but apparently Binary Streams are not supported:
W !, tDynamicObj.%ToJSON()
^
<METHOD NOT SUPPORTED>zTestByteArray+9^UCLH.SIP.Test.Utilities.1 *%ToJSON,%Stream.FileBinary
We have contacted the vendor of the API to double check if they are able to process an E64 encoded version of the file content, but they are expecting it as a byte array so our choices here are zero to none. If we use POSTMAN and copy across the contents of the file into a JSON request the call works like a charm.
Is anybody aware of any workaround available? I have tried to output the %Stream.GlobalBinary as a String but it logically outputs the content of the pdf file a a stream of characters:
"FileContent":"%PDF-1.4\r\n%âãÃÃ\r\n1 0 obj\r\n <<\r\n \/Author()\/Title()\/Subject()\/Producer()\/Keywords()\/CreationDate(D:20190114132345+00'00')\/ModDate(D:20190114132345+00'00')\/Creator(CSSystems Corporation)\r\n >>\r\nendobj\r\n4 0 obj\r\n <<\/Filter \/FlateDecode \/Length 1314>>\r\n stream\r\nxÂÃÂmoÃ6¿øomÂ4!%R\"Âa@....
Hello Ivan,
This question requires some investigation and would be better served as a WRC Issue. Please forward these details onto support@intersystems.com and an available Advisor will assist you with this error.
Did you ever get an answer on this. I'm currently struggling with the same issue. One solution is to build the entire JSON by writing strings to the stream, for a lot of reasons, this is not an ideal solution.
I think the solution is to use character stream and made manual Base64Encoding/Decoding.
I have created two methods for it:
/// Flags - 1 - Do not insert CR/LF after every 76 characters.<br>
ClassMethod Base64Encode(tIn As %Stream.TmpBinary, Output tOut As %Stream.TmpCharacter, chunk As %Integer = 32000, Flags As %Integer = 1) As %Status
{
set sc = $$$OK
if $g(tIn)="" quit $$$ERROR(5001, "Input stream required")
if '$IsObject(tIn) quit $$$ERROR(5001,"Input is not a stream object")
if 'tIn.%IsA("%Stream.Object") quit $$$ERROR(5001,"Input object is not a stream")
If '$IsObject($g(tOut)) {
set tOut=##class(%Stream.TmpCharacter).%New()
}
if 'tOut.%IsA("%Stream.Object") quit $$$ERROR(5001,"Output object is not a stream")
set chunk=chunk-(chunk#3)
do tIn.Rewind()
While 'tIn.AtEnd {
set sc= tOut.Write($SYSTEM.Encryption.Base64Encode(tIn.Read(chunk),Flags))
if 'sc Quit
}
Quit sc
}
ClassMethod Base64Decode(tIn As %Stream.TmpBinary, Output tOut As %Stream.TmpCharacter, chunk As %Integer = 32000) As %Status
{
set sc = $$$OK
if $g(tIn)="" quit $$$ERROR(5001,"Input stream required")
if '$IsObject(tIn) quit $$$ERROR(5001,"Input is not a stream object")
if 'tIn.%IsA("%Stream.Object") quit $$$ERROR(5001,"Input object is not a stream")
If '$IsObject($g(tOut)) {
set tOut=##class(%Stream.TmpCharacter).%New()
}
if 'tOut.%IsA("%Stream.Object") quit $$$ERROR(5001,"Output object is not a stream")
set chunk=chunk-(chunk#4)
do tIn.Rewind()
While 'tIn.AtEnd {
set sc= tOut.Write($SYSTEM.Encryption.Base64Decode(tIn.Read(chunk)))
if 'sc Quit
}
Quit sc
}
Then in the code you need to call it e.g.:
where 'tPDF' is a stream object with original PDF content, tDocRequest.FileContent then will contain given PDF encoded to Base64 - this is what binary streams do automatically.