如何在.NET中使用XmlWriter创建XmlDocument?

52

许多.NET函数使用XmlWriter来输出/生成xml。将输出到文件/字符串/内存中是一个非常重要的操作:

XmlWriter xw = XmlWriter.Create(PutYourStreamFileWriterEtcHere);
xw.WriteStartElement("root");
...
有时候,您需要操作生成的Xml,因此希望将其加载到XmlDocument中,或者出于其他某种原因需要XmlDocument,但必须使用XmlWriter生成XML。例如,如果调用一个仅输出到XmlWriter的第三方库中的函数。
你可以做的其中一件事是将xml写入字符串,然后将其加载到XmlDocument中:
StringWriter S = new StringWriter();
XmlWriter xw = XmlWriter.Create(S);
/* write away */
XmlDocument xdoc = new XmlDocument();
xdoc.LoadXml(S.ToString());

然而这种方法效率低下——首先需要将所有的XML信息序列化为字符串,然后再解析该字符串以创建DOM。

如何使XmlWriter直接构建XmlDocument?

5个回答

94

这里至少有一种解决方案:

XmlDocument doc = new XmlDocument(); 
using (XmlWriter writer = doc.CreateNavigator().AppendChild()) 
{ 
    // Do this directly 
     writer.WriteStartDocument(); 
     writer.WriteStartElement("root"); 
     writer.WriteElementString("foo", "bar"); 
     writer.WriteEndElement(); 
     writer.WriteEndDocument();
    // or anything else you want to with writer, like calling functions etc.
}

显然,当你调用AppendChild()时,XpathNavigator会给你一个XmlWriter。

感谢Martin Honnen在这里的贡献:http://groups.google.com/group/microsoft.public.dotnet.xml/browse_thread/thread/24e4c8d249ad8299?pli=1


1
谢谢!对我来说很明显你可以使用XmlWriter作为XmlDocument的构建器,但我找不到一个简单的方法来实现它,所以最终我只能写入缓冲区并再次解析。他们本应该让这个过程更容易找到。 - Tomek Szpakowicz
3
请注意,按照原文所写,生成的 XmlWriter 将具有 Document 的一致性级别,因此如果您将其作为 XSL 变换的输入(并且您不是创建完整文档),则可以使用 new XmlDocument().CreateFragment().CreateNavigator() 来代替。 - harpo
2
请注意,这种方法的一个缺点是,对于基于XPathNavigator的XmlWriter,WriteRaw的行为不同 - 即它会转义输出。 - harpo
最后(因为我仍在使用它),请注意导航器会丢弃 DOCTYPE。不确定是否有解决方法。 - harpo
1
这太棒了。可以非常快速、优雅地构建一个 XmlDocument,而不必经过字符串或流的处理。一个有用的示例:将任意 WPF 运行时实例直接转换为它所根源的 XAML 对象图形的 XmlDocument 表示:var xd = new XmlDocument(); var w = xd.CreateNavigator().AppendChild(); XamlWriter.Save(inst, new XamlDesignerSerializationManager(w) { XamlWriterMode = XamlWriterMode.Expression }); (请确保使用 System.Windows.Markup 中的 XamlWriter)。这显然使其他替代方案看起来非常笨拙。太棒了! - Glenn Slayden
显示剩余2条评论

11

你可以做相反的事情:首先使用 DOM 构建 XmlDocument,然后将其写入 XmlWriter

XmlDocument xdoc = new XmlDocument();
... // build the document

StringWriter S = new StringWriter();
XmlWriter xw = XmlWriter.Create(S);
xdoc.WriteTo(xw);

4
XmlWriter不是设计用来写入XmlDocument的,而是设计用来写入流(Stream)或文本编写器(TextWriter)的。你所要求的根本不可能实现,我只是建议另一种方法... - Thomas Levesque
2
+1 给无意义的踩票数加一。在我看来,这是对所提问的问题的一个不错的回答。 - Binary Worrier
2
@Tomas Levesque:我的观点是,有人问一个问题“我该如何解决X”,而答案是“不要解决X,改为解决Y”,但这并不是问题的答案。如果无法解决,那么这就是答案。 - Seb Nilsson
2
@Seb:我强烈不同意。如果每次需要从A到C而没有直接路径时我们都停下来,作为程序员我们将走不远。我们的职业依赖于能够找到通过B的替代路径。Thomas提出了一种替代方法,老实说,我不明白为什么要谴责这个答案(是的,我知道这是一个社区。是的,我已经行使了我的投票权。但是我有权表达对普遍回应的不满)。 - Binary Worrier
1
很高兴找到一个能够双向转换的补充答案。由于使用了两种不同的方法来整合两个独立的程序集,我需要进行来回转换。谢谢! - TamusJRoyce
显示剩余8条评论

8
您可以使用XMLWriter类来编写XML文件。以下是一个示例。
    XmlWriterSettings objSetting = new XmlWriterSettings();
    objSetting.Indent = true;
    objSetting.NewLineOnAttributes = true;

    System.Text.StringBuilder sb = new System.Text.StringBuilder();


    using (XmlWriter objWriter = XmlWriter.Create(sb, objSetting))
    {
        //Note the artificial, but useful, indenting
        objWriter.WriteStartDocument();

        objWriter.WriteStartElement("books");

        ////////Start Book Element///////

        objWriter.WriteStartElement("book");

        objWriter.WriteStartAttribute("ISBN");
        objWriter.WriteValue("asp1");
        objWriter.WriteEndAttribute();

        objWriter.WriteStartElement("Title");
        objWriter.WriteValue("ASP.NET");
        objWriter.WriteEndElement();

        objWriter.WriteElementString("ReleaseDate", "11/11/2010");

        objWriter.WriteStartElement("Pages");
        objWriter.WriteValue(200);
        objWriter.WriteEndElement(); //price

        objWriter.WriteEndElement(); //book
        ////////End Book Element///////


        ////////Another Element

        ////////Start Book Element///////

        objWriter.WriteStartElement("book");

        objWriter.WriteStartAttribute("ISBN");
        objWriter.WriteValue("c#2");
        objWriter.WriteEndAttribute();

        objWriter.WriteStartElement("Title");
        objWriter.WriteValue("C#.NET");
        objWriter.WriteEndElement();

        objWriter.WriteElementString("ReleaseDate", "10/11/2010");

        objWriter.WriteStartElement("Pages");
        objWriter.WriteValue(500);
        objWriter.WriteEndElement(); 

        objWriter.WriteEndElement(); //book
        ////////End Book Element///////



        objWriter.WriteEndElement(); //books
        objWriter.WriteEndDocument();

    }

    File.WriteAllText(Server.MapPath("BooksList.xml"), sb.ToString());

4
XmlWriter的设计理念是在修改数据完成之后再开始写入。但它并不适合你的情况。要么在写入前确定好数据,要么继续当前做法。

5
我不同意。XmlWriter是建造者模式的一个很好的例子,使用它构建DOM(XmlDocument或任何其他实现)非常合理。我有一个非常真实的用例: 我读取XML数据并使用一些XslCompiledTransform进行转换,它将数据写入XmlWriter,直接构造XmlDocument,我可以处理然后写出(可能在输出时应用某些其他XSLT)。这有什么问题吗?而将数据写出到流中再解析是:a)无意义的(它不会帮助或使设计更清晰),b)不必要的和c)高度低效的。 - Tomek Szpakowicz

1

有一个底层的 Stream 对象,XmlWriter 正在向其中写入内容,如果它是双向的 (如 MemoryStrem),你可以简单地将其重新定位到 -0-,然后在 XmlDocument.Load(stream) 中使用该 Stream 对象。

希望有所帮助。

Z


这将导致相同的低效率 - 重新解析流以再次构建DOM。 - Boaz
这是我选择的方法,与问题中给出的示例不同,因为不需要创建字符串作为中间变量。 - stevehipwell
Boaz是正确的,这基本上与从字符串解析XmlDocument相同。 - Thomas Levesque

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接