Conversion from %Stream.GlobalBinary to %CharacterStream
Hi,
I'm receiving in one of our interface a XML file as a %Stream.GlobalBinary, and in the source Application that produces this XML file, the user sometimes add a special character that force the XML file to have the header encoding to UTF-8, instead of ISO-8859-1 :
<?xml version="1.0" encoding="UTF8"?>
When my current interface tries to convert the %Stream.GlobalBinary into %XML.XPATH.Document to this exceptional UTF8 header encoding of the XML file :
Set tSC=##class(%XML.XPATH.Document).CreateFromStream(stream,.tDoc) --> contains exceptionally UTF8 encoding
I have the following bug that blocks the whole interface :
invalid byte 'd' at position 2 of a 3-bytes sequence in at line 1 offset 39 (method zCreateFromStream)
So the idea I got is to create the following method I'm developping that forces to change the encoding to ISO-8859-1 of the XML file through the method ChangeXMLStreamEncoding :
Method CheckANSIXMLHeader(pStream As %Stream.GlobalBinary) As %Stream.GlobalBinary
{
#dim inputCharStream As %CharacterStream
#dim outputCharStream As %CharacterStream
#dim outputGlobalBinStream As %Stream.GlobalBinarySet tSC = $$$OK
//first convert %Stream.GlobalBinary to %CharacterStream back
Set outputCharStream = ##class(Ens.Util.XML.Reader).ChangeXMLStreamEncoding(inputCharStream, "ISO-8859-1", , tSC)
//finally convert %CharacterStream back to %Stream.GlobalBinary
Quit outputGlobalBinStream
}
This method ChangeXMLStreamEncoding uses %CharacterStream only.
So my problem is that I don't know how to convert %Stream.GlobalBinary to %CharacterStream, and vice-versa
Could you tell me how to perform such conversion ?
Thanks a lot
Frédéric Donnet-Monay, integrator, Centre Hospitalier Universitaire Vaud, Lausanne, Switzerland
Comments
You have to cast your %CharacterStream to a specific implementation, say, to %GlobalCharacterStream. The latter one implements %GlobalStreamAdaptor, which inherits from %AbstractStream and %Stream.Object. Finally, the %Stream.Object class exposes the CopyFrom method which allows you to copy data from your %Stream.GlobalBinary instance (and vice versa)
I'm not sure I understand your issue right, my understanding is that you receive an XML declared as UTF-8 that contains ISO-8859-1 characters.
If so, you can convert the encoding of the stream content using something like:
/// Convert the encoding of the content of a stream to UTF-8./// If OutStream is not passed (of any %Stream.* class), then a new %Stream.GlobalBinary is returned./// Note that OutStream IS NOT SAVED on exitClassMethod ConvertStreamToUTF8(InStream As%Stream.Object, ByRef OutStream As%Stream.Object = {##class(%Stream.GlobalBinary).%New()}) As%Status
{
Set sc=$$$OKSet MaxRead=$$$MaxLocalLength\2; to be safeTry {
While 'InStream.AtEnd {
Set content=InStream.Read(MaxRead)
Set sc=OutStream.Write($ZCONVERT(content,"O","UTF8"))
If$$$ISERR(sc) Quit
}
} Catch CatchError {
#dim CatchError as%Exception.SystemExceptionSet sc=CatchError.AsStatus()
}
Quit sc
}
If the problem is different, please provide more info/details, maybe a sample XML (not necessarily the original XML).
Enrico
Thanks for yours answers, I tried your solutions, but it is hard and did not give good results.
To simplify my explanation : I'm reading an XML file from a file-share, and I want to change the XML header encoding from UTF8 to ISO-8859-1, if it happens sometimes.
So the result of my new method should transform the potential header
<?xml version="1.0" encoding="UTF8"?>
to a new header
<?xml version="1.0" encoding="ISO-8859-1"?>
Set inputBinaryStream=##class(%Stream.FileBinary).%New()
Set inputBinaryStream.Filename="\\server\your\share\file.xml"
Set outputBinaryStream=##class(%Stream.FileBinary).%New()
Set outputBinaryStream = ##class(Ens.Util.XML.Reader).ChangeXMLStreamEncoding(inputBinaryStream, "ISO-8859-1",outputBinaryStream, .tSC)
; Since the output stream is passed, you can just call (last line) with:
Do ##class(Ens.Util.XML.Reader).ChangeXMLStreamEncoding(inputBinaryStream, "ISO-8859-1",outputBinaryStream, .tSC)
Enrico
Thank you Enrico for your quick answer. I'm almost there.
To be more precise, we access the fileshare through a EnsLib.FTP.OutboundAdapter, through the method ..Adapter.GetStream(pRequest.StringValue, .tStream) .
pRequest.StringValue contains the file-share path of the file I want to return.
Here is the full method :
/// Return the CDA XML file as a %Stream.GlobalBinary
Method GetSoarianXMLCDA(pRequest As Ens.StringRequest, pResponse As %Stream.GlobalBinary) As %Status
{
// Create the variable tSC (temporary Status Code)
Set tSC = $$$OK
try{
//Create the stream
Set tStream=##class(%Stream.GlobalBinary).%New()
//set the files path to the FTP Adapter
Set tSC = ..Adapter.GetStream(pRequest.StringValue, .tStream)
If $$$ISERR(tSC){
$$$LOGERROR("Error while retrieving Stream from CDA file : tSC = " _ tSC)
}
$$$ASSERT($IsObject(tStream))
//call here Do ##class(Ens.Util.XML.Reader).ChangeXMLStreamEncoding...
If tStream.SizeGet() > 0{
Set pResponse = tStream
}
else{
$$$LOGERROR("CDA file " _ pRequest.StringValue _" is empty or null")
}
}
catch ex {
$$$LOGERROR(##class(%SYSTEM.Status).GetErrorText(ex.AsStatus()))
Set tSC = ex.AsStatus()
}
Quit tSC
}
The FTP adapter method ..Adapter.GetStream returns a %GlobalBinaryStream If I read the adapter's code correctly.
Could you please tell me how to use ChangeXMLStreamEncoding() in this methode above ?
Thanks again
Fred
Simply replace the line:
//call here Do ##class(Ens.Util.XML.Reader).ChangeXMLStreamEncoding...
With:
Set outputBinaryStream=##class(%Stream.FileBinary).%New()
Do ##class(Ens.Util.XML.Reader).ChangeXMLStreamEncoding(tStream, "ISO-8859-1",outputBinaryStream, .tSC)
//you may want to check tSC, just in case....
//now on use the header changed outputBinaryStream instead of tStream
Enrico