0

I found a weird behavior with XmlSerializer in C#, an someone help me? I want this XML in SOAP/MTOM, and I need the xop:Include outside any other XMLElement:

<Root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <xop:Include href="cid:http://tempuri.org/1/635913361387292553" xmlns:xop="http://www.w3.org/2004/08/xop/include"/>
    <List>1</List>
    <List>2</List>
</Root>

Here my C# code:

public static void Main(string[] args)
{
    var simulatedFile = new byte[800];
    new Random().NextBytes(simulatedFile);
    var root = new Root {List = new List<string> {"1","2"}, Bytes = simulatedFile};

    XmlSerializer ser = new XmlSerializer(typeof(Root));
    using (var mtomInMemory = new MemoryStream())
    {
        using (var writer = XmlDictionaryWriter.CreateMtomWriter(mtomInMemory, Encoding.UTF8, int.MaxValue, string.Empty))
        {
            ser.Serialize(writer, root);
        }
        Console.WriteLine(Encoding.UTF8.GetString(mtomInMemory.ToArray()));
    }

    Console.Read();
}
public class Root
{
    [XmlText]
    public byte[] Bytes { get; set; }
    [XmlElement]
    public List<string> List { get; set; }
}

The Error I have is:

'System.InvalidOperationException' occurred in System.Xml.dll There was an error reflecting type 'Program.Root'. Cannot serialize object of type 'Program.Root'. Consider changing type of XmlText member 'Program.Root.Bytes' from System.Byte[] to string or string array.

But I need to let my Bytes array to contain file binary because with MTOM, it will be serialized by the CreateMtomWriter in base64 if the Length is less that 768 byte or in xop:Include if more than 768 bytes.

I want it to be encoded as a direct child of Root Element, not wrapped in any other Element.

If in the class Root, i put only the Bytes or the List property, it works perfectly, but if i put both, it does not.

Val
  • 65
  • 2
  • 12

2 Answers2

1

You appear to have encountered an XmlSerializer bug.

If I change the class to remove the List property, like so:

public class Root
{
    [XmlText]
    public byte[] Bytes { get; set; }
}

Then XmlSerializer will serialize it successfully with the byte array automatically represented as a base 64 string:

<Root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">AAECAwQFBgcICQ==</Root>

And instead, if I change the class to make the XmlText property be a string-valued proxy property that translates the byte array from and to base 64, then XmlSerializer will again serialize it successfully and identically:

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

    [XmlText]
    public string Base64Bytes
    {
        get
        {
            return Bytes == null ? null : Convert.ToBase64String(Bytes);
        }
        set
        {
            Bytes = value == null ? null : Convert.FromBase64String(value);
        }
    }

    [XmlElement]
    public List<string> List { get; set; }
}

Indeed, the documentation for XmlTextAttribute.DataType clearly indicates that byte arrays are one of the data types supported by [XmlText]. Neverthess, the combination of an [XmlText] byte [] Byte { get; set; } property with any other [XmlElement] property in the same class causes the XmlSerializer constructor to throw an exception. You might want to report a problem to Microsoft since there is no reason for this not to work.

In the meantime, you can use the workaround of a string-valued proxy property as is shown above.

dbc
  • 104,963
  • 20
  • 228
  • 340
0

From Microsoft: Thanks for reporting this issue. This is by design. The array of bytes cannot be text value. Please try to workaround this issue e.g. using string.

Val
  • 65
  • 2
  • 12