Search code examples
delphiutf-8base64delphi-7encode

Delphi7 Base64 encode UTF8 XML


I'm still using Delphi7 (I know) and I need to encode an UTF8 XML in Base64 format.

I create the XML using IXMLDocument, which support UTF8 (that is, if I save to a file).

Since I'm using Indy10 to HTTP Post the XML request, I tried using TIdEncoderMIME to Base64 encode the XML. But some UTF8 chars are not encoded well.

Try1:

XMLText := XML.XML.Text;
EncodedXML := TIdEncoderMIME.EncodeBytes(ToBytes(XMLText));

In the above case most probably some UTF8 information/characters are already lost when the XML is saved to a string.

Try2:

XMLStream := TMemoryStream.Create;
XML.SaveToStream(XMLStream);
EncodedXML := TIdEncoderMIME.EncodeStream(XMLStream);
//or
EncodedXML := TIdEncoderMIME.EncodeStream(XMLStream, XMLStream.Size);

Both of the above gives back EncodedXML = '' (empty string).

What am I doing wrong?


Solution

  • Try using the TIdEncoderMIME.EncodeString() method instead. It has an AByteEncoding parameter that you can use to specify the desired byte encoding that Indy should encode the string characters as, such as UTF-8, before it then base64 encodes the resulting bytes:

    XMLText := XML.XML.Text;
    EncodedXML := TIdEncoderMIME.EncodeString(XMLText, IndyTextEncoding_UTF8);
    

    Also note that in Delphi 2007 and earlier, where string is AnsiString, there is also an optional ASrcEncoding that you can use the specify the encoding of the AnsiString (for instance, if it is already UTF-8), so that it can be decoded to Unicode properly before then being encoded to the specified byte encoding (or, in the case where the two encodings are the same, the AnsiString can be base64 encoded as-is):

    XMLText := XML.XML.Text;
    EncodedXML := TIdEncoderMIME.EncodeString(XMLText, IndyTextEncoding_UTF8, IndyTextEncoding_UTF8);
    

    You are getting data loss when using EncodeBytes() because you are using ToBytes() without specifying any encoding parameters for it. ToBytes() has similar AByteEncoding and ASrcEncoding parameters.

    In the case where you tried to encode a TMemoryStream, you simply forgot to reset the stream's Position back to 0 after calling SaveToStream(), so there was nothing for EncodeStream() to encode. That is why it returned a blank base64 string:

    XMLStream := TMemoryStream.Create;
    try
      XML.SaveToStream(XMLStream);
      XMLStream.Position := 0; // <-- add this
      EncodedXML := TIdEncoderMIME.EncodeStream(XMLStream);
    finally
      XMLStream.Free;
    end;