none
How to use DataContractSerializer to serialize multiple objects along with other mixed data? RRS feed

  • Question

  • My goal is to be able to serialize multiple objects to a stream with the ability to mix other kinds of binary data with it. (I am using DataContract, DataMember and DataContractSerializer to serialize the data.)

    For example, I might serialize some binary data, followed by an XML-serialized object, followed by some other binary data and then another XML-serialized object.

    Clearly I can do this by prefixing each XML-serialized object by a binary length, and then when reading them in I could read the binary length, then read the XML data into a MemoryStream or a string and deserialize from that.

    However, I was hoping to get the DataContractSerializer to handle this, but it doesn't quite work how I'd hoped.

    The essence of the problem is that when you call DataContractSerializer.ReadObject(stream), it reads all the way to the end of the stream, even if there's a load of extra data at the end. Furthermore, if any of that data isn't XML, it throws an exception.

    My question is: Is there any way to get the ReadObject() method to stop reading when it reaches the (well-defined!) end of the XML.

    Sample code will make this clearer. Consider the following program:

    (1) Run the program and look at the total stream length and the stream position after deserializing the first object. They are the same, showing that the deserialization has read the entire contents of the stream
    (2) Uncomment the mem.WriteByte(0) line. Now when you run the program it will crash because ReadObject() reads all the extra data, the last byte of which is now an illegal XML character.

    Does anyone have any ideas or thoughts? Thanks!

    using System;
    using System.IO;
    using System.Runtime.Serialization;
    using System.Text;
    using System.Xml;
    
    
    namespace ConsoleApplication1
    {
      class Program
      {
        public static void Main(string[] args)
        {
          Demo demo1 = new Demo { Text = "Text1", Number = 1 };
          Demo demo2 = new Demo { Text = "Text2", Number = 2 };
    
          using (var mem = new MemoryStream())
          {
            mem.WriteByte(1); // Write a couple of "junk" bytes at the start.
            mem.WriteByte(2);
    
            demo1.Serialize(mem);
            long startOfSecondObject = mem.Length; // Need to remember this. :(
    
            demo2.Serialize(mem);
    
            // If you uncomment the following line, the deserialization of the second
            // object to demo4 will fail below:
    
            // mem.WriteByte(0); // Write an extra junk byte to the end.
    
            Console.WriteLine("Total stream length = " + mem.Length);
            Console.WriteLine("Serialized to: \n");
            UTF8Encoding encoding = new UTF8Encoding();
            Console.WriteLine(encoding.GetString(mem.ToArray()));
    
            mem.Position = 2; // Skip "junk" bytes.
            var demo3 = Demo.Deserialize(mem);
            Console.WriteLine("Total stream length = " + mem.Length);
            Console.WriteLine("Stream position after reading first object = " + mem.Position);
    
            Console.WriteLine("\nDeserialized data 1:\n" + demo3);
    
            // Here's the problem: After deserializing the first object, the stream position
            // has been advanced to the end of the stream rather than to the first byte following
            // the first object's serialized data.
            //
            // I get around the problem by storing the offset of the second object, and using
            // it below. The question is: How to avoid having to do this? How do you stop
            // DataContractSerializer.ReadObject() from reading the entire contents of the stream?
    
            mem.Position = startOfSecondObject;
            var demo4 = Demo.Deserialize(mem);
            Console.WriteLine("\nDeserialized data 2:\n" + demo4);
          }
        }
      }
    
      [DataContract(Namespace="")]
      class Demo
      {
        [DataMember]
        public string Text { get; set; }
    
        [DataMember]
        public int Number { get; set; }
    
        public void Serialize(Stream stream)
        {
          var serializer = new DataContractSerializer(this.GetType());
          var settings  = new XmlWriterSettings { CloseOutput = false, Indent = true };
    
          using (var xmlStream = XmlWriter.Create(stream, settings))
          {
            serializer.WriteObject(xmlStream, this);
          }
    
          stream.WriteByte((byte)'\n'); // Add a newline.
        }
    
        public static Demo Deserialize(Stream stream)
        {
          var serializer = new DataContractSerializer(typeof(Demo));
          var settings  = new XmlReaderSettings { CloseInput = false };
    
          using (var xmlStream = XmlReader.Create(stream, settings ))
          {
            return (Demo)serializer.ReadObject(xmlStream);
          }
        }
    
        public override string ToString()
        {
          return string.Format("Demo: Text = {0}, Number = {1}", Text, Number);
        }
      }
    }
    
    

    • Moved by John SaundersModerator Thursday, April 28, 2011 4:07 PM WCF Question (From:ASMX Web Services and XML Serialization)
    Thursday, April 28, 2011 12:01 PM

Answers

  • Hello, DataContractSerializer will not handle this automatically. You have to manually separate the original stream to several smaller streams and then use DataContractSerializer.
    Lante, shanaolanxing This posting is provided "AS IS" with no warranties, and confers no rights.
    Windows Azure Technical Forum Support Team Blog
    • Marked as answer by Yi-Lun Luo Wednesday, May 4, 2011 4:55 AM
    Friday, April 29, 2011 5:02 AM

All replies