我需要将对象序列化为 XML 文件,但有一些格式限制:
- 没有命名空间数据。
- 第一行中没有编码标记。
- 根元素必须缩进。
我编写的以下代码负责限制 1 和 2:
public void WriteToXml(TextWriter writer)
{
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
using (XmlWriter xmlWriter = XmlWriter.Create(writer, new XmlWriterSettings { OmitXmlDeclaration = true, Indent = true, IndentChars = "t" }))
{
new XmlSerializer(typeof(CalledUnit)).Serialize(xmlWriter, this, ns);
}
}
并得到以下结果:
<CalledUnit>
<Caller>some info</Caller>
<CallerId>1001</CallerId>
<Called>some more info</Called>
<CalledId>31</CalledId>
</CalledUnit>
但我想要以下结果(注意缩进):
<CalledUnit>
<Caller>some info</Caller>
<CallerId>1001</CallerId>
<Called>some more info</Called>
<CalledId>31</CalledId>
</CalledUnit>
有什么想法可以解决这个问题吗?
如果文件不大,则可以序列化为内存流,然后逐行从流中读取并将其写入文件,同时以所需的空格缩进为前缀。如果需要代码示例,请注释。
首先,这不是一个真正的XML问题 - XML并不真正关心这样的空格问题。
但是,您可以通过自己进行格式化来实现此目的。 在 WriteToXml
方法中,不是直接写入TextWriter
,而是写入StringBuilder
,然后对字符串运行正则表达式,将rn
替换为 rn
+ 要填充它的空格数。 或者,您可以使用类似StringReader.ReadLine()
的内容读取流,并将其输出(以空格为前缀)写入TextWriter
。
没有明显的方法来使用 .Net XML 类来做到这一点。(例如,XmlTextWriter.Indent()
不是虚拟的,如参考源所示。 您可能需要将 XML 流式传输到中间表示形式,然后对其进行修补。 但是,与其使用正则表达式来执行修补(这可能会导致 CDATA 节点出现问题),不如使用 Mark Fussell 的WriteShallowNode
将 XmlReader 和 XmlWriter 类组合在一起进行简单的流式处理转换来执行修补:
public void WriteToXml(TextWriter writer)
{
var ns = new XmlSerializerNamespaces();
ns.Add("", "");
var settings = new XmlWriterSettings { OmitXmlDeclaration = true, Indent = true, IndentChars = "t" };
string xml;
using (var tempWriter = new StringWriter())
{
using (XmlWriter xmlWriter = XmlWriter.Create(tempWriter, settings))
new XmlSerializer(this.GetType()).Serialize(xmlWriter, this, ns);
xml = tempWriter.ToString();
}
using (var reader = new StringReader(xml))
using (var xmlReader = XmlReader.Create(reader))
{
XmlNodeType? prevType = null;
using (var xmlWriter = XmlWriter.Create(writer, settings))
{
while (xmlReader.Read())
{
if ((xmlReader.NodeType == XmlNodeType.Element || xmlReader.NodeType == XmlNodeType.EndElement)
&& (prevType == null || prevType == XmlNodeType.Whitespace))
{
xmlWriter.WriteWhitespace(settings.IndentChars); // Add one more indentation
}
xmlWriter.WriteShallowNode(xmlReader);
prevType = xmlReader.NodeType;
}
}
}
}
然后,将Mark Fussell的代码改编为扩展方法:
public static class XmlWriterExtensions
{
public static void WriteShallowNode(this XmlWriter writer, XmlReader reader)
{
// adapted from http://blogs.msdn.com/b/mfussell/archive/2005/02/12/371546.aspx
if (reader == null)
throw new ArgumentNullException("reader");
if (writer == null)
throw new ArgumentNullException("writer");
switch (reader.NodeType)
{
case XmlNodeType.Element:
writer.WriteStartElement(reader.Prefix, reader.LocalName, reader.NamespaceURI);
writer.WriteAttributes(reader, true);
if (reader.IsEmptyElement)
{
writer.WriteEndElement();
}
break;
case XmlNodeType.Text:
writer.WriteString(reader.Value);
break;
case XmlNodeType.Whitespace:
case XmlNodeType.SignificantWhitespace:
writer.WriteWhitespace(reader.Value);
break;
case XmlNodeType.CDATA:
writer.WriteCData(reader.Value);
break;
case XmlNodeType.EntityReference:
writer.WriteEntityRef(reader.Name);
break;
case XmlNodeType.XmlDeclaration:
case XmlNodeType.ProcessingInstruction:
writer.WriteProcessingInstruction(reader.Name, reader.Value);
break;
case XmlNodeType.DocumentType:
writer.WriteDocType(reader.Name, reader.GetAttribute("PUBLIC"), reader.GetAttribute("SYSTEM"), reader.Value);
break;
case XmlNodeType.Comment:
writer.WriteComment(reader.Value);
break;
case XmlNodeType.EndElement:
writer.WriteFullEndElement();
break;
default:
Debug.WriteLine("unknown NodeType " + reader.NodeType);
break;
}
}
}