【问题标题】:XML indenting when injecting an XML string into an XmlWriter将 XML 字符串注入 XmlWriter 时的 XML 缩进
【发布时间】:2009-05-13 15:24:51
【问题描述】:

我有一个写入文件的 XmlTextWriter 和一个使用该文本编写器的 XmlWriter。此文本编写器设置为输出制表符缩进的 XML:

XmlTextWriter xtw = new XmlTextWriter("foo.xml", Encoding.UTF8);
xtw.Formatting = Formatting.Indented;
xtw.IndentChar = '\t';
xtw.Indentation = 1;

XmlWriter xw = XmlWriter.Create(xtw);

根据 Jeff 的 MSDN 链接更改:

XmlWriterSettings set = new XmlWriterSettings();
set.Indent = true;
set.IndentChars = "\t";
set.Encoding = Encoding.UTF8;

xw = XmlWriter.Create(f, set);

这不会改变最终结果。


现在我在我的 XmlWriter 中的任意深度,我从其他地方(我无法控制)获取一个 XML 字符串,它是一个单行、非缩进的 XML。如果我调用 xw.WriteRaw() 则该字符串被逐字注入并且不遵循我想要的缩进。

...
string xml = ExternalMethod();
xw.WriteRaw(xml);
...

基本上,我想要一个可以解析 XML 字符串并遍历所有 WriteStartElement 等的 WriteRaw,以便根据 XmlTextWriter 的设置对其进行重新格式化。

我的偏好是使用我已有的设置来执行此操作,并且无需重新加载最终的 XML 来重新格式化它。我也不想用 XmlReader 之类的东西解析 XML 字符串,然后模仿它在我的 XmlWriter 中找到的内容(非常非常手动的过程)。

最后,我宁愿有一个简单的解决方案,也不愿遵循我的喜好。 (当然,最好的解决方案很简单,并且符合我的喜好。)

【问题讨论】:

    标签: c# .net xml


    【解决方案1】:

    使用 XmlReader 将 xml 作为 xml 节点读取怎么样?

    string xml = ExternalMethod();
    XmlReader reader =  XmlReader.Create(new StringReader(xml));
    xw.WriteNode(reader, true);
    

    【讨论】:

    • 这仅适用于原始 XML 未格式化/缩进的情况。您应该可以通过提供一些忽略空格的XmlReaderSettings 来解决此问题。
    • 可以,但是要确保xml不包含XML声明元素,否则你可能会得到ArgumentException: Cannot write XML declaration. WriteStartDocument method has already written it.
    • 另一个提示,经过数小时弄清楚为什么这对我不起作用(它正在编写 xml 内联而不是在下一个缩进行开始第一个元素):我正在给 XmlReader我使用 XmlWriter 将某个对象序列化为 MemoryStream 得到的字符串,然后使用 Encoding.UTF8.GetString(stream.ToArray()) 获取字符串。但是(只有在将字符串放入十六进制编辑器后才会出现这种情况),Encoding.UTF8.GetString 将 ef bb bf 标记插入到字符串中,并且由于某种原因导致不同的处理。
    【解决方案2】:

    您不应使用 XmlTextWriter,如 MSDN 中所述:

    在 .NET Framework 2.0 版中 发布,推荐的做法是 使用创建 XmlWriter 实例 XmlWriter.Create 方法和 XmlWriterSettings 类。这允许 你要充分利用所有的 在此介绍的新功能 发布。有关详细信息,请参阅 创建 XML 编写器。

    相反,您应该使用XmlWriter.Create 来获取您的作者。然后,您可以使用 XmlWriterSettings 类指定缩进等内容。

    XmlWriterSettings settings = new XmlWriterSettings();
    settings.Indent = true;
    settings.IndentChars = "\t";
    

    更新

    我认为你可以只使用 WriteNode。您将 xml 字符串加载到 XDocument 或 XmlReader 中,然后使用其中的节点将其写入 XmlWriter。

    【讨论】:

    • 这解决了我使用 WriteRaw() 的缩进问题?
    • 没有变化。将 XML 字符串逐字写入输出。
    【解决方案3】:

    这是我目前为止最好的。一个非常手动的过程,只支持所写的内容。我的字符串 XML 只不过是标签、属性和文本数据。如果它支持命名空间、CDATA 等,那么它必须相应地增长。

    非常手动,非常混乱,很可能容易出现错误,但它确实满足了我的喜好。

    private static void PipeXMLIntoWriter(XmlWriter xw, string xml)
    {
        byte[] dat = new System.Text.UTF8Encoding().GetBytes(xml);
        MemoryStream m = new MemoryStream();
        m.Write(dat, 0, dat.Length);
        m.Seek(0, SeekOrigin.Begin);
        XmlReader r = XmlReader.Create(m);
    
        while (r.Read())
        {
            switch (r.NodeType)
            {
                case XmlNodeType.Element:
                    xw.WriteStartElement(r.Name);
    
                    if (r.HasAttributes)
                    {
                        for (int i = 0; i < r.AttributeCount; i++)
                        {
                            r.MoveToAttribute(i);
                            xw.WriteAttributeString(r.Name, r.Value);
                        }
                    }
    
                    if (r.IsEmptyElement)
                    {
                        xw.WriteEndElement();
                    }
                    break;
                case XmlNodeType.EndElement:
                    xw.WriteEndElement();
                    break;
                case XmlNodeType.Text:
                    xw.WriteString(r.Value);
                    break;
    
                default:
                    throw new Exception("Unrecognized node type: " + r.NodeType);
            }
        }
    }
    

    【讨论】:

      【解决方案4】:

      组成上面的答案我发现这是可行的:

      private static string FormatXML(string unformattedXml) {
          // first read the xml ignoring whitespace
          XmlReaderSettings readeroptions= new XmlReaderSettings {IgnoreWhitespace = true};
          XmlReader reader = XmlReader.Create(new StringReader(unformattedXml),readeroptions);
      
          // then write it out with indentation
          StringBuilder sb = new StringBuilder();
          XmlWriterSettings xmlSettingsWithIndentation = new XmlWriterSettings { Indent = true};                       
          using (XmlWriter writer = XmlWriter.Create(sb, xmlSettingsWithIndentation)) {
              writer.WriteNode(reader, true);
          }
      
          return sb.ToString();
      }
      

      【讨论】:

        【解决方案5】:

        怎么样:

        string xml = ExternalMethod();
        var xd = XDocument.Parse(xml);
        xd.WriteTo(xw);
        

        【讨论】:

          【解决方案6】:

          我一直在寻找这个问题的答案,但在 VB.net 中。

          感谢 Colin Burnett,我解决了这个问题。我做了两个更正:首先,XmlReader 必须忽略空格(settings.IgnoreWhiteSpaces);其次,阅读器在读取属性后必须回到元素中。下面你可以看到代码的样子。

          我也尝试了 GreyCloud 的解决方案,但是在生成的 XML 中有一些烦人的空属性(xlmns)。

          Private Sub PipeXMLIntoWriter(xw As XmlWriter, xml As String)
              Dim dat As Byte() = New System.Text.UTF8Encoding().GetBytes(xml)
              Dim m As New MemoryStream()
              m.Write(dat, 0, dat.Length)
              m.Seek(0, SeekOrigin.Begin)
              Dim settings As New XmlReaderSettings
              settings.IgnoreWhitespace = True
              settings.IgnoreComments = True
              Dim r As XmlReader = XmlReader.Create(m, settings)
          
              While r.Read()
                    Select Case r.NodeType
                          Case XmlNodeType.Element
                              xw.WriteStartElement(r.Name)
          
                              If r.HasAttributes Then
                                  For i As Integer = 0 To r.AttributeCount - 1
                                      r.MoveToAttribute(i)
                                      xw.WriteAttributeString(r.Name, r.Value)
                                  Next
                                  r.MoveToElement()
                              End If
          
                              If r.IsEmptyElement Then
                                  xw.WriteEndElement()
                              End If
                              Exit Select
                          Case XmlNodeType.EndElement
                              xw.WriteEndElement()
                              Exit Select
                          Case XmlNodeType.Text
                              xw.WriteString(r.Value)
                              Exit Select
                          Case Else
          
                              Throw New Exception("Unrecognized node type: " + r.NodeType)
                      End Select
                End While
          End Sub
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2021-03-04
            • 2015-04-30
            • 1970-01-01
            • 2016-05-14
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多