-->

Building an XML file iteratively with XmlWriter

2019-07-27 22:35发布

问题:

I want to be able to use XmlWriter (C#, .NET) to create an XML document with multiple calls to a method that generates XML and then a single call to a final method that closes everything off. When I try to call this method multiple times:

private void SiblingGenerator(List<XmlNode> XMLList, XmlWriter textWriter,
    string newPath, FileInfo fi)
{
    if (fi.Length == 0)
    {
        MessageBox.Show("file doesn't exist");

        textWriter.WriteStartDocument();
        textWriter.WriteStartElement("batch");
        //...
    }

    // ...
}

...it returns an error saying that WriteStartDocument needs to be the first call.

It seems like the calls to the textWriter aren't actually being written because on each subsequent call the document starts over again.

Can anyone tell me why this is happening?

回答1:

A XmlWriter is forward only and cannot be reused. You shouldn't call WriteStartDocument multiple times on the same instance of this class. Because it is an argument of your method it is the caller that must take care of handling the life-cycle of the writer.

using (var writer = XmlWriter.Create("foo.xml"))
{
    SiblingGenerator(XMLList, writer, newPath, fi);
}

If you need to reuse the SiblingGenerator function multiple times then you might want to externalize the call of the WriteStartDocument method:

using (var writer = XmlWriter.Create("foo.xml"))
{
    writer.WriteStartDocument();
    SiblingGenerator(XMLList, writer, newPath, fi);
    SiblingGenerator(XMLList, writer, newPath, fi);
    ...
    writer.WriteEndDocument();
}


回答2:

Any kind of "writer" object like TextWriter, XmlWriter, etc do some level of buffering as does the underlying stream which is entirely out of its control. In other words, you can't rely on the length of the underlying file to determine whether or not prior calls to the writer have been made.

But specifically speaking, there is a Flush method available on many of the classes in System.IO such as Stream and TextWriter that can be used to push the buffered contents to disk but this makes for pretty fragile code, in my opinion.

You should maintain some other kind of state to determine whether or not you have already written the start of the document.



回答3:

There are several things that can keep that method from working. The XmlWriter might not write the code to the stream directly, the FileStream might not write the data to the file directly, and the FileInfo object caches it's information, so you would have to make it update itself to get information that is up to date.

Instead of using a FileInfo object to check for this, you could just use a boolean variable to keep track of whether it's the first item or not.

Alternatively, just write the start of the document before going into the loop that calls this method.



回答4:

I would guess that something else is wrong, that code should work. You're probably reusing a XmlWriter. If you declare the XmlWriter locally in the method do you still get the error?