For some reason neither .NET's XmlSerializer nor DataContractSerializer allow reading data through an XmlReader. These APIs work other way round writing data into an XmlWriter. To get data through XmlReader one needs to write it to some destination like a file or memory stream, and then to read it using XmlReader. This complicates streaming design considerably.
XmlSerializer
DataContractSerializer
XmlReader
XmlWriter
In fact the very same happens with other .NET APIs.
We think the reason of why .NET designers preferred XmlWriter to XmlReader in those APIs is that XmlReader's implementation is a state machine like, while XmlWriter's implementation looks like a regular procedure. It's much harder to manually write and to support a correct state machine logic than a procedure.
If history would have gone slightly different way, and if yield return, lambda, and Enumerator API appeared before XmlReader, and XmlWriter then, we think, both these classes looked differently. Xml source would have been described with a IEnumerable<XmlEvent> instead of XmlReader, and XmlWriter must be looked like a function receiving IEnumerable<XmlEvent>. Implementing XmlReader would have meant a creating a enumerator. Yield return and Enumerable API would have helped to implement it in a procedural way.
IEnumerable<XmlEvent>
But in our present we have to deal with the fact that DataContractSerializer should write the data into XmlWriter, so let's assume we have a project that uses Entity Framework to access the database, and that you have a data class Person, and data access method GetPeople():
Person
GetPeople()
[DataContract(Name = "person", Namespace = "http://www.nesterovsky-bros.com")] public class Person { [DataMember] public int Id { get; set; } [DataMember] public string FirstName { get; set; } [DataMember] public string LastName { get; set; } [DataMember] public string City { get; set; } [DataMember] public string Title { get; set; } [DataMember] public DateTime BirthDate { get; set; } [DataMember] public int Age { get; set; } } public static IEnumerable<Person> GetPeople() { ... }
And your goal is to expose result of GetPeople() as XmlReader. We achieve result with three simple steps:
JoinedStream
Stream
IEnumerable<Stream>
The code is rather simple, so here we qoute its essential part:
public static class Extensions { public static Stream JoinStreams(this IEnumerable<Stream> streams, bool closeStreams = true) { return new JoinedStream(streams, closeStreams); } public static Stream ToXmlStream<T>( this IEnumerable<T> items, string rootName = null, string rootNamespace = null) { return items.ToXmlStreamParts<T>(rootName, rootNamespace). JoinStreams(false); } private static IEnumerable<Stream> ToXmlStreamParts<T>( this IEnumerable<T> items, string rootName = null, string rootNamespace = null) { if (rootName == null) { rootName = "ArrayOfItems"; } if (rootNamespace == null) { rootNamespace = ""; } var serializer = new DataContractSerializer(typeof(T)); var stream = new MemoryStream(); var writer = XmlDictionaryWriter.CreateTextWriter(stream); writer.WriteStartDocument(); writer.WriteStartElement(rootName, rootNamespace); writer.WriteXmlnsAttribute("s", XmlSchema.Namespace); writer.WriteXmlnsAttribute("i", XmlSchema.InstanceNamespace); foreach(var item in items) { serializer.WriteObject(writer, item); writer.WriteString(" "); writer.Flush(); stream.Position = 0; yield return stream; stream.Position = 0; stream.SetLength(0); } writer.WriteEndElement(); writer.WriteEndDocument(); writer.Flush(); stream.Position = 0; yield return stream; } private class JoinedStream: Stream { public JoinedStream(IEnumerable<Stream> streams, bool closeStreams = true) ... } }
The use is even more simple:
// We have a streamed business data. var people = GetPeople(); // We want to see it as streamed xml data. using(var stream = people.ToXmlStream("persons", "http://www.nesterovsky-bros.com")) using(var reader = XmlReader.Create(stream)) { ... }
We have packed the sample into the project Streaming.zip.
In the next post we're going to remind about streaming processing in xslt.
Remember Me
a@href@title, b, blockquote@cite, em, i, strike, strong, sub, super, u