views:

1266

answers:

2

Hello everyone,

I am looking for a sample, which could let me deserialize an XML stream encoded in UTF-8 into a field of a class. More specifically, I have a class like,

class Foo
{
    string abc;
    byte[] bcd;
}

and abc maps to XML element "Abc" and bcd maps to XML element "Bcd", and I want to get the stream for bcd and retrieve bytes (from XML stream for related element "Bcd" directly) to manipulate manually/in a customized way.

I am looking for a sample, but failed, could anyone help to point me to a related sample or wrote some pseudo code?

Edit: the XML is SOAP reaponse from server, in the response there is one response XML element (element Bcd in my sample) which is encoded by UTF-8 from server side, but since Http Web Services will use base64 at client side, so each time I receive such "bytes" at client side and the automatically generated web services proxy will throw exception says invalid XML element in base64 encoding. So, I am thinking about how to overwrite the default using base64 encoding to decode the bytes, and this is why I ask this question. If there could be a way to accept stream or something similar represents the on-wire bytes of the related response elements (Bcd in my sample) and let me manipulate, it will be so great!

thanks in advance, George

+4  A: 

This article is very good at explaining how to handle XML serialization in C#.

Andrew Hare
Thanks Andrew cool document, not what 100% what I want to have. I need to manipulate the underlying stream directly, since the stream is sent from server, but in Http the default encoding is base64, I want to overwrite base64 to UTF-8 decoding by myself. Any related stream manipulation samples?
George2
A: 

IXmlSerializable is one option (but not much fun), but it feels like in this case properties offer most of what is wanted without extra work:

[Serializable]
public class Foo
{
    public string Abc {get;set;}
    private byte[] bcd;
    public byte[] Bcd {
        get {return bcd;}
        set {/* your code (with `value`) here */ }
    }
}

Re the wire format (base64 etc); have you tried just running it through xsd.exe? It might surprise you and get it right... but yes; within IXmlSerializable you will have your choice of ReadContentAsBase64, ReadContentAsBinHex, etc. But it is very ugly; something like (for a simple version):

[Serializable]
public class Foo : IXmlSerializable
{
    public Foo() { }
    public Foo(string abc, byte[] bcd)
    {
        Abc = abc;
        Bcd = bcd;
    }
    public string Abc { get; private set; }
    public byte[] Bcd { get; private set; }

    XmlSchema IXmlSerializable.GetSchema()
    {
        return null;
    }

    void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer)
    {
        if (Abc != null)
        {
            writer.WriteElementString("Abc", Abc);
        }
        if (Bcd != null)
        {
            writer.WriteStartElement("Bcd");
            writer.WriteBase64(Bcd, 0, Bcd.Length);
            writer.WriteEndElement();
        }
    }
    void IXmlSerializable.ReadXml(System.Xml.XmlReader reader)
    {
        reader.ReadStartElement();
        reader.ReadStartElement("Abc");
        Abc = reader.ReadString();
        reader.ReadEndElement();
        reader.ReadStartElement("Bcd");
        MemoryStream ms = null;
        byte[] buffer = new byte[256];
        int bytesRead;
        while ((bytesRead = reader.ReadContentAsBase64(
            buffer, 0, buffer.Length)) > 0)
        {
            if (ms == null) ms = new MemoryStream(bytesRead);
            ms.Write(buffer, 0, bytesRead);
        }
        if (ms != null) Bcd = ms.ToArray();
        reader.ReadEndElement(); 
    }
}
Marc Gravell
Cool, Marc! Nice to see you here. :-)A quick question, why you make set private? And when/who will call ReadXML/WriteXML?
George2
And why you call read start element twice here? reader.ReadStartElement(); reader.ReadStartElement("Abc");
George2
The first ReadStartElement consumes the "Foo" element
Marc Gravell
XmlSerializer will call ReadXml / WriteXml. The "private set" was just because I wanted it readable externally (compare to your example where *everything* was private). It could be public {get;set;} happily.
Marc Gravell
Thanks Marc, you used ReadContentAsBase64, but the input byte stream is encoded in UTF-8. So, I think I should not use ReadContentAsBase64, any comments what method should be used?
George2
1. "XmlSerializer will call ReadXml / WriteXml. " -- I think you mean the .Net deserialization process will call ReadXml/WriteXml automatically?2. I suspect if you mark set method private, how could .Net deserializer (not part of class Foo) set value?
George2
No, I meant what I said. They are explicit interface implementations, so are actually part of the public API; XmlSerializer just has to cast as IXmlSerializable. And *because* we implement IXmlSerializable, XmlSerializer **doesn't** set the value directly; it just calls ReadXml
Marc Gravell
Re encoding - do you have an example of what this supposed utf8/bute[] data looks like? Maybe we're all talking cross-purposes...
Marc Gravell
Thanks Marc, I want to confirm with you that when a SOAP response is received from server, the XML Serializer will be invoked automatically by underlying .Net runtime, which invokes WriteXML (I think when response is received, it should be WriteXML and not ReadXML be called)?
George2
Yes, the coded character from server side is like the England pound, which is not in the normal ASCII range, previously, my code could display all ASCII correctly, but ? for non-ASCII. I suspect from SOAP response, the deserialization is using a wrong deserialization approach. Any comments?
George2