How to serialize byte array to XML using XmlSerializer in C#?

Ilan Huberman picture Ilan Huberman · Mar 21, 2011 · Viewed 22.8k times · Source

Say we have a struct that it's data is provided by un-managed byte array using Marshal.PtrToStructure.

The C# struct layout:

[StructLayout(LayoutKind.Sequential, Size = 128, CharSet = CharSet.Ansi, Pack = 1)]
public struct MNG_Y_Params
{
    public byte Number;
    public byte Version;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
    public byte[] OliNumber;
    public byte InterfaceType;
}

The byte array represent a (ascii) string in the un-managed code.

This struct is a member of another struct (that has some other members):

public struct MyData
{
    public int ID;
    public StructType structType;
    [XmlElement(ElementName="MNG_Y_Params")]
    public MNG_Y_Params y_params;
    [XmlElement(ElementName = "SimpleStruct2")]
    public SimpleStruct2 ss2;
};

So we also have this support code

public class XMLIgnore
{
    static public XmlSerializer customserialiser(MyData d)
    {
        XmlAttributes attrs = new XmlAttributes();
        attrs.XmlIgnore = true;
        XmlAttributeOverrides xmlOveride = new XmlAttributeOverrides();
        switch (d.structType)
        {
            case StructType.ST_1:
                xmlOveride.Add(typeof(MyData), "ss2", attrs);
                break;
            case StructType.ST_2:
                xmlOveride.Add(typeof(MyData), "y_params", attrs);
                break;
            default:
                break;
        }
        return new XmlSerializer(typeof(MyData), xmlOveride);
    }
}

and the save method

    static void SaveToXml(object obj, string fileName, XmlSerializer writer)
    {
        //XmlSerializer writer = new XmlSerializer(obj.GetType());
        using (StreamWriter file = new StreamWriter(fileName))
        {
            writer.Serialize(file, obj);
        }
    }

For the example we'd just generate some data.

        MNG_Y_Params yParams = new MNG_Y_Params();
        yParams.Version = 1;
        yParams.InterfaceType = 15;
        yParams.Number = 35;
        ASCIIEncoding enc = new ASCIIEncoding();
        yParams.OliNumber = enc.GetBytes("#1");

        MyData md1 = new MyData();
        md1.ID = 1;
        md1.structType = StructType.ST_1;
        md1.y_params = yParams;

        XmlSerializer writer = XMLIgnore.customserialiser(md1);
        SaveToXml(md1, @"C:\temp\dataOne.xml", writer);

Expected XML:

<?xml version="1.0" encoding="utf-8"?>
<MyData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <ID>1</ID>
  <structType>ST_1</structType>
  <MNG_Y_Params>
    <Number>35</Number>
    <Version>1</Version>
    <OliNumber>#1</OliNumber>
    <InterfaceType>15</InterfaceType>
  </MNG_Y_Params>
</MyData>

Result XML:

<?xml version="1.0" encoding="utf-8"?>
<MyData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <ID>1</ID>
  <structType>ST_1</structType>
  <MNG_Y_Params>
    <Number>35</Number>
    <Version>1</Version>
    <OliNumber>IzE=</OliNumber>
    <InterfaceType>15</InterfaceType>
  </MNG_Y_Params>
</MyData>

Please focus on the byte array member (OliNumber) result.

Is there an attribute that we could use here? What am I missing?

Thanks for your time and help.

Ilan

For reference

Answer

SwDevMan81 picture SwDevMan81 · Mar 21, 2011

The XmlSerializer by default will encode the byte arrays using base 64 encoding. If you use this site and paste in IzE=, and decode it, the result will be #1. You can change the encoding by setting the XmlElementAttribute.DataType. I'm not sure if [XmlElement(DataType = "string")] will work, but you can try it. Using [XmlElement(DataType = "hexBinary")] will generate the raw bytes.