views:

65

answers:

1

Is it possible to have a single stream and have more than one XmlWriter write to that stream and end up with well-formed XML?

I have an object hierarchy in which each object is solely and fully responsible for serializing itself. My latest attempt has been to create a stream at the highest level, then pass that stream reference down and allow each object to create its own XmlWriter to serialize itself to the stream. However, this ends up creating nodes within incomplete parent nodes (the start element is not fully formed in the parent before child content is written, even with a flush).

There are multiple appdomains, so passing the XmlWriter reference will not work. I was having each object return a string and writing that raw XML string into the stream, but some of the strings get to be very, very long (collections). That's why I decided on a stream -- so that each object could write out small pieces at a time and a stream instance is both serializable and MBR.

I decided against using XmlSerializer for reasons I didn't seem to document. But I'm going to trust my earlier judgment on that.

Thanks for anything that can lead to a more thorough understanding of what I'm working with.

A: 

You might want to just write the xml parts per class as strings to the stream instead of using the XmlWriter. This allows you full control over ending tags. But I've created a workaround which seems to work. This allows you to pass the stream which is serializable between appdomains.

First off a helper class to initialize new XmlWriters and fixing the stream before we pass it on.

public static class XmlWriterExt
{
    /// <summary>
    /// Make sure any previous tag is ended by writing dummy text, then backtracking the position
    /// </summary>
    public static void PrepareStream(this XmlWriter writer, Stream stream)
    {
        writer.WriteElementString("x", string.Empty);
        writer.Flush();
        stream.Position -= 5; //backtrack the dummy element
    }

    /// <summary>
    /// Get an xml writer which works on fragments and without the xml declaration
    /// </summary>
    public static XmlWriter GetWriter(Stream stream)
    {
        XmlWriterSettings settings = new XmlWriterSettings();
        settings.OmitXmlDeclaration = true;
        settings.ConformanceLevel = ConformanceLevel.Fragment;
        XmlWriter xmlWriter = XmlWriter.Create(stream, settings);
        return xmlWriter;
    }
}

Here's a couple of test classes, nested in each other.

class TopClass
{
    InnerClass _innerClass = new InnerClass();

    public void Serialize(Stream stream)
    {
        XmlWriter xmlWriter = XmlWriterExt.GetWriter(stream);
        xmlWriter.WriteStartElement("top");
        xmlWriter.PrepareStream(stream);
        _innerClass.Serialize(stream);
        xmlWriter.WriteEndElement();
        xmlWriter.Flush();
    }
}

class InnerClass
{
    public void Serialize(Stream stream)
    {
        XmlWriter xmlWriter = XmlWriterExt.GetWriter(stream);
        xmlWriter.WriteElementString("b", "testing");
        xmlWriter.Flush();
    }
}

Test code

MemoryStream ms = new MemoryStream();
TopClass top = new TopClass();
top.Serialize(ms);

string result = Encoding.UTF8.GetString(ms.ToArray());

and result

<top>
  <b>testing</b>
</top>
Mikael Svenson
ConformanceLevel.Fragment! Argh! This is why I post -- I learn something new every time.
oakskc