C#:如何在将对象写入 XML 时向根元素添加缩进



我需要将对象序列化为 XML 文件,但有一些格式限制:

  1. 没有命名空间数据。
  2. 第一行中没有编码标记。
  3. 根元素必须缩进。

我编写的以下代码负责限制 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;
        }
    }
}

最新更新