4
\$\begingroup\$

I'm using XML to store data and I need to store a string array in it. The best way (that I can think of) to store a string array is by converting it to a single string. In order to do that, I'm using string.Join to join the strings with \0 and then use Base64 so that the \0 (and any other special character) can be retrieved easier. Because it's possible that a string in a array already has \0 in it (causing another string to be the array when they're converted back to an array), I'm escaping the existing \0 by first adding another backslash to existing backslashes and changing \0 to \\0. Is there any better way to do this? Should I be using \r\n instead (but wouldn't that also be the same as using \0)? I should also note that its so I can use IXmlSerializable and it is also storing byte[], uint, ulong and string in the XML as well.

Retrieving String Array (Via XML)

this.Name = reader.GetAttribute("Name");

string strType = reader.GetAttribute("Type");

byte[] buffer = new byte[1000];
int readBytes = 0;
MemoryStream ms = new MemoryStream();
BinaryWriter bw = new BinaryWriter(ms);
while ((readBytes = reader.ReadElementContentAsBase64(buffer, 0, 50)) > 0)
{
    bw.Write(buffer, 0, readBytes);
}

bw.Close();

List<string> unescapedStrs = new List<string>();
string[] val = strValue.Split('\0');
string newStr;

foreach (string s in val)
{
    newStr = Regex.Replace(s, @"([^\\])(\\0)", "$1\0").Replace("\\\\", "\\");

    unescapedStrs.Add(newStr);
}

if (val.Length > 0)
    this.Value = unescapedStrs.ToArray();

Storing String Array (Via XML)

writer.WriteAttributeString("Name", this.Name);

writer.WriteAttributeString("Type", this.Type);

string strValue = string.Empty;

string[] val = (string[])this.Value;

List<string> escapedStrs = new List<string>();

foreach (string s in val)
{
    string newStr = s;

    // Escape backslashes so existing \\0 don't get mixed up
    if (newStr.IndexOf('\\') > 0)
        newStr = newStr.Replace("\\", "\\\\");

    // If theres NULL chars in strings already, escape them
    if (newStr.IndexOf('\0') > 0)
        newStr = newStr.Replace("\0", "\\0");

    escapedStrs.Add(newStr);
}

strValue = string.Join("\0", escapedStrs.ToArray());

int byteLen = Encoding.Unicode.GetByteCount(strValue);
byte[] bytes = Encoding.Unicode.GetBytes(strValue);
writer.WriteBase64(bytes, 0, byteLen);
\$\endgroup\$
6
  • \$\begingroup\$ You need to get familiar with System.XML namespace. In particular XMLReader, XMLWriter, XMLDocument \$\endgroup\$ – radarbob Jul 15 '14 at 18:45
  • \$\begingroup\$ I am familiar. Why do you think Im using Base64? \$\endgroup\$ – ub3rst4r Jul 15 '14 at 19:09
  • \$\begingroup\$ ok. Couldn't the string[] be a set of nested XML elements? So, then, I thought I'd see some XML element/node stuff going on rather than string joining \$\endgroup\$ – radarbob Jul 15 '14 at 19:16
  • \$\begingroup\$ Is there a reason why you wouldn't use XMLSerializer / XMLDeserializer? \$\endgroup\$ – fabigler Jul 15 '14 at 20:48
  • \$\begingroup\$ @fabigler I am using XMLSerializer and XMLDeserializer \$\endgroup\$ – ub3rst4r Jul 15 '14 at 21:22
2
\$\begingroup\$

My first reaction is: don't do this.

By creating your own encoding for an array of strings, your XML is not going to be (easily) consumable by other programs. By Base64 encoding your strings, your XML is not going to be human-readable.

These are two major benefits of XML, and if you're willing to sacrifice them, maybe XML is not the right format. If the serialized data is only going to be consumed by the same program, binary serialization would be a better option.

That said, if you want to serialize a list of strings to XML, this is what I would recommend:

public class Email
{
    private static readonly XmlSerializer Serializer = new XmlSerializer(typeof(Email));

    [XmlArrayItem("Attachment")]
    public List<string> Attachments { get; set; }

    public XDocument ToXDocument()
    {
        var document = new XDocument();
        using (var writer = document.CreateWriter())
        {
            Serializer.Serialize(writer, this);
        }

        return document;
    }
}

And some sample client code:

var email = new Email
{
    Attachments = new List<string> { "<hello></hello>", "asdf" }
};
var xml = email.ToXDocument().ToString();
Console.WriteLine(xml);

This will output

<Email xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Attachments>
    <Attachment>&lt;hello&gt;&lt;/hello&gt;</Attachment>
    <Attachment>asdf</Attachment>
  </Attachments>
</Email>

Now, this chokes if the strings contain a null-byte, which you mentioned was a possibility. It's hard to say what to do about this without knowing more about your problem domain. Hopefully, stripping null-bytes is an option for you.

Serializing a byte[] as a Base64-encoded string can be done using a surrogate property:

[XmlIgnore]
public byte[] Bytes { get; set; }

public string BytesBase64
{
    get { return Convert.ToBase64String(this.Bytes); }
    set { this.Bytes = Convert.FromBase64String(value); }
}
\$\endgroup\$
3
  • \$\begingroup\$ I was looking at binary serialization, but somewhere (I believe SO) it said it wasnt recommended and to use XML serialization instead? Any idea why? \$\endgroup\$ – ub3rst4r Jul 16 '14 at 4:38
  • 2
    \$\begingroup\$ @ub3rst4r I can't really guess at the reasoning behind that. You need to consider the specifics of your situation and the trade-offs inherent in different types of serialization. I'm not saying binary serialization is right for you, just that if you are willing to give up interoperability and readability, it might be a better (smaller, faster) option. If you do go with XML, don't implement a custom encoding for arrays of strings. \$\endgroup\$ – mjolka Jul 16 '14 at 4:52
  • 1
    \$\begingroup\$ @ub3rst4r There are also other binary serialization formats, like Protocol Buffers. \$\endgroup\$ – svick Jul 16 '14 at 16:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.