【问题标题】:What is the simplest way to get indented XML with line breaks from XmlDocument?从 XmlDocument 中获取带有换行符的缩进 XML 的最简单方法是什么?
【发布时间】:2010-09-17 05:46:41
【问题描述】:

当我使用XmlDocument 从头开始​​构建 XML 时,OuterXml 属性的所有内容都已经很好地缩进了换行符。但是,如果我在一些非常“压缩”的 XML(没有换行符或缩进)上调用 LoadXml,那么 OuterXml 的输出将保持这种状态。所以...

XmlDocument 的实例中获取美化 XML 输出的最简单方法是什么?

【问题讨论】:

    标签: c# .net xmldocument outerxml


    【解决方案1】:

    根据其他答案,我查看了XmlTextWriter 并提出了以下帮助方法:

    static public string Beautify(this XmlDocument doc)
    {
        StringBuilder sb = new StringBuilder();
        XmlWriterSettings settings = new XmlWriterSettings
        {
            Indent = true,
            IndentChars = "  ",
            NewLineChars = "\r\n",
            NewLineHandling = NewLineHandling.Replace
        };
        using (XmlWriter writer = XmlWriter.Create(sb, settings)) {
            doc.Save(writer);
        }
        return sb.ToString();
    }
    

    这比我希望的要多一些代码,但它工作得很好。

    【讨论】:

    • 您甚至可以考虑将实用程序方法创建为 XmlDocument 类的扩展方法。
    • 奇怪的是,对我来说,除了将 xml 标头的编码设置为 UTF-16 之外,这没有任何作用。奇怪的是,即使我明确设置了settings.Encoding = Encoding.UTF8;,它也会这样做
    • 编码问题可以通过使用MemoryStream+StreamWriter而不是StringBuilder来解决,用enc.GetString(memstream.GetBuffer(), 0, (int)memstream.Length);获取文本。但是,最终结果仍然没有被格式化。是否与我从已具有格式的已读文档开始有关?我只是希望我的新节点也被格式化。
    • 我很想将"\r\n"修改为Environment.Newline
    • doc.PreserveWhitespace 不应设置为 true。否则,如果它已经包含部分缩进,它将失败。
    【解决方案2】:

    改编自Erika Ehrli's博客,应该这样做:

    XmlDocument doc = new XmlDocument();
    doc.LoadXml("<item><name>wrench</name></item>");
    // Save the document to a file and auto-indent the output.
    using (XmlTextWriter writer = new XmlTextWriter("data.xml", null)) {
        writer.Formatting = Formatting.Indented;
        doc.Save(writer);
    }
    

    【讨论】:

    • using 语句的关闭将在调用Dispose() 时自动关闭编写器。
    • 对我来说,这只缩进一行。我还有几十行没有缩进。
    【解决方案3】:

    如果您可以访问 Linq,甚至更容易

    try
    {
        RequestPane.Text = System.Xml.Linq.XElement.Parse(RequestPane.Text).ToString();
    }
    catch (System.Xml.XmlException xex)
    {
                displayException("Problem with formating text in Request Pane: ", xex);
    }
    

    【讨论】:

    • 非常好! thumbs up 优于已接受答案的优点是它不会产生 XML 注释,因此更适合 XML 片段
    • 奇怪的是,这会从 XML 中删除 &lt;?xml ...?&gt;&lt;!DOCTYPE ...&gt;。片段可以,但不适合完整文档。
    • 这是唯一对我有用的方法。使用 xmltextwriter、Formatting = Formatting.Indented 和 XmlWriterSettings 的所有其他方法都不会重新格式化文本,但此方法会。
    【解决方案4】:

    更短的扩展方法版本

    public static string ToIndentedString( this XmlDocument doc )
    {
        var stringWriter = new StringWriter(new StringBuilder());
        var xmlTextWriter = new XmlTextWriter(stringWriter) {Formatting = Formatting.Indented};
        doc.Save( xmlTextWriter );
        return stringWriter.ToString();
    }
    

    【讨论】:

    • 这非常好用,并且不涉及在磁盘上创建不必要的文件
    【解决方案5】:

    如果为已包含 XmlProcessingInstruction 子节点的 XmlDocument 调用上述 Beautify 方法,则会引发以下异常:

    无法编写 XML 声明。 WriteStartDocument 方法已经 写的。

    这是我对原始版本的修改版本,以摆脱异常:

    private static string beautify(
        XmlDocument doc)
    {
        var sb = new StringBuilder();
        var settings =
            new XmlWriterSettings
                {
                    Indent = true,
                    IndentChars = @"    ",
                    NewLineChars = Environment.NewLine,
                    NewLineHandling = NewLineHandling.Replace,
                };
    
        using (var writer = XmlWriter.Create(sb, settings))
        {
            if (doc.ChildNodes[0] is XmlProcessingInstruction)
            {
                doc.RemoveChild(doc.ChildNodes[0]);
            }
    
            doc.Save(writer);
            return sb.ToString();
        }
    }
    

    它现在对我有用,可能您需要扫描所有子节点以查找 XmlProcessingInstruction 节点,而不仅仅是第一个?


    2015 年 4 月更新:

    由于我遇到了另一种编码错误的情况,因此我搜索了如何在没有 BOM 的情况下强制执行 UTF-8。我找到了this blog post,并基于它创建了一个函数:

    private static string beautify(string xml)
    {
        var doc = new XmlDocument();
        doc.LoadXml(xml);
    
        var settings = new XmlWriterSettings
        {
            Indent = true,
            IndentChars = "\t",
            NewLineChars = Environment.NewLine,
            NewLineHandling = NewLineHandling.Replace,
            Encoding = new UTF8Encoding(false)
        };
    
        using (var ms = new MemoryStream())
        using (var writer = XmlWriter.Create(ms, settings))
        {
            doc.Save(writer);
            var xmlString = Encoding.UTF8.GetString(ms.ToArray());
            return xmlString;
        }
    }
    

    【讨论】:

    • 如果你把 cdata 部分放在父节点内部和子节点之前,它将不起作用
    • MemoryStream 似乎不需要,至少在我这边。在我设置的设置中:Encoding = Encoding.UTF8OmitXmlDeclaration = true
    【解决方案6】:
    XmlTextWriter xw = new XmlTextWriter(writer);
    xw.Formatting = Formatting.Indented;
    

    【讨论】:

      【解决方案7】:
          public static string FormatXml(string xml)
          {
              try
              {
                  var doc = XDocument.Parse(xml);
                  return doc.ToString();
              }
              catch (Exception)
              {
                  return xml;
              }
          }
      

      【讨论】:

      • 下面的答案肯定可以做一些解释,但它对我有用,而且比其他解决方案简单得多。
      • 看来您需要导入 system.link.XML 程序集才能在 PS 3 上运行。
      【解决方案8】:

      一个简单的方法是使用:

      writer.WriteRaw(space_char);
      

      就像这个示例代码一样,这个代码是我用来使用 XMLWriter 创建树状视图的结构:

      private void generateXML(string filename)
              {
                  using (XmlWriter writer = XmlWriter.Create(filename))
                  {
                      writer.WriteStartDocument();
                      //new line
                      writer.WriteRaw("\n");
                      writer.WriteStartElement("treeitems");
                      //new line
                      writer.WriteRaw("\n");
                      foreach (RootItem root in roots)
                      {
                          //indent
                          writer.WriteRaw("\t");
                          writer.WriteStartElement("treeitem");
                          writer.WriteAttributeString("name", root.name);
                          writer.WriteAttributeString("uri", root.uri);
                          writer.WriteAttributeString("fontsize", root.fontsize);
                          writer.WriteAttributeString("icon", root.icon);
                          if (root.children.Count != 0)
                          {
                              foreach (ChildItem child in children)
                              {
                                  //indent
                                  writer.WriteRaw("\t");
                                  writer.WriteStartElement("treeitem");
                                  writer.WriteAttributeString("name", child.name);
                                  writer.WriteAttributeString("uri", child.uri);
                                  writer.WriteAttributeString("fontsize", child.fontsize);
                                  writer.WriteAttributeString("icon", child.icon);
                                  writer.WriteEndElement();
                                  //new line
                                  writer.WriteRaw("\n");
                              }
                          }
                          writer.WriteEndElement();
                          //new line
                          writer.WriteRaw("\n");
                      }
      
                      writer.WriteEndElement();
                      writer.WriteEndDocument();
      
                  }
      
              }
      

      这样您就可以按照您通常习惯的方式添加制表符或换行符,即 \t 或 \n

      【讨论】:

        【解决方案9】:

        在实施此处发布的建议时,我遇到了文本编码问题。似乎XmlWriterSettings 的编码被忽略了,并且总是被流的编码覆盖。当使用StringBuilder 时,这始终是 C# 内部使用的文本编码,即 UTF-16。

        所以这是一个支持其他编码的版本。

        重要提示:如果您的XMLDocument 对象在加载文档时启用了preserveWhitespace 属性,则格式将被完全忽略。这让我有一段时间感到困惑,所以请确保不要启用它。

        我的最终代码:

        public static void SaveFormattedXml(XmlDocument doc, String outputPath, Encoding encoding)
        {
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;
            settings.IndentChars = "\t";
            settings.NewLineChars = "\r\n";
            settings.NewLineHandling = NewLineHandling.Replace;
        
            using (MemoryStream memstream = new MemoryStream())
            using (StreamWriter sr = new StreamWriter(memstream, encoding))
            using (XmlWriter writer = XmlWriter.Create(sr, settings))
            using (FileStream fileWriter = new FileStream(outputPath, FileMode.Create))
            {
                if (doc.ChildNodes.Count > 0 && doc.ChildNodes[0] is XmlProcessingInstruction)
                    doc.RemoveChild(doc.ChildNodes[0]);
                // save xml to XmlWriter made on encoding-specified text writer
                doc.Save(writer);
                // Flush the streams (not sure if this is really needed for pure mem operations)
                writer.Flush();
                // Write the underlying stream of the XmlWriter to file.
                fileWriter.Write(memstream.GetBuffer(), 0, (Int32)memstream.Length);
            }
        }
        

        这将使用给定的文本编码将格式化的 xml 保存到磁盘。

        【讨论】:

        • preserveWhitespace 破坏了 XmlWriter 的格式化功能这一事实是一个重要的信息——这让我困惑了很长一段时间。谢谢!
        【解决方案10】:

        如果你有一个 XML 字符串,而不是一个可以使用的文档,你可以这样做:

        var xmlString = "<xml>...</xml>"; // Your original XML string that needs indenting.
        xmlString = this.PrettifyXml(xmlString);
        
        private string PrettifyXml(string xmlString)
        {
            var prettyXmlString = new StringBuilder();
        
            var xmlDoc = new XmlDocument();
            xmlDoc.LoadXml(xmlString);
        
            var xmlSettings = new XmlWriterSettings()
            {
                Indent = true,
                IndentChars = " ",
                NewLineChars = "\r\n",
                NewLineHandling = NewLineHandling.Replace
            };
        
            using (XmlWriter writer = XmlWriter.Create(prettyXmlString, xmlSettings))
            {
                xmlDoc.Save(writer);
            }
        
            return prettyXmlString.ToString();
        }
        

        【讨论】:

          【解决方案11】:

          基于公认答案的更简化的方法:

          static public string Beautify(this XmlDocument doc) {
              StringBuilder sb = new StringBuilder();
              XmlWriterSettings settings = new XmlWriterSettings
              {
                  Indent = true
              };
          
              using (XmlWriter writer = XmlWriter.Create(sb, settings)) {
                  doc.Save(writer);
              }
          
              return sb.ToString(); 
          }
          

          不需要设置新行。缩进字符也有默认的两个空格,所以我不想设置它。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2020-10-16
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-02-13
            相关资源
            最近更新 更多