将XML字符串注入XmlWriter时的XML缩进

我有一个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); ... 

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

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

在结束时,我宁愿有一个简单的解决方案,而不是遵循我的偏好。 (当然,最好的解决方案很简单,并且符合我的偏好。)

如何使用XmlReader将xml读取为xml节点?

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

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

在.NET Framework 2.0版中,建议的做法是使用XmlWriter.Create方法和XmlWriterSettings类创建XmlWriter实例。 这使您可以充分利用此版本中引入的所有新function。 有关更多信息,请参阅创建XML Writer。

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

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

更新

我想你可以使用WriteNode。 您获取xml字符串并将其加载到XDocument或XmlReader中,然后使用该节点将其写入XmlWriter。

这是迄今为止我所做的最好的。 一个非常手动的过程,只支持所写的内容。 我的字符串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); } } } 

撰写上面的答案,我发现这是有效的:

 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(); } 

我正在寻找这个问题的答案,但在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 

怎么样:

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