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

时间:2015-04-26 15:39:58

标签: c# xml xml-serialization

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

  1. 没有命名空间数据。
  2. 第一行没有编码标记。
  3. 根元素必须缩进。
  4. 我编写的以下代码负责限制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>
    

    有任何想法如何解决这个问题?

3 个答案:

答案 0 :(得分:1)

如果文件不大,您可以序列化为内存流,然后逐行从流中读取并将其写入文件,同时为前缀添加所需的空格缩进。如果您需要代码示例,请注释。

答案 1 :(得分:1)

首先,这不是一个真正的XML问题 - XML并不真正关心这样的空白问题。

但是,你可以通过自己进行格式化来实现这一点。在WriteToXml方法中,不要直接写入TextWriter,而是写信给StringBuilder,然后在字符串上运行正则表达式,将\r\n替换为\r\n +要填充它的空格数。或者,您可以使用StringReader.ReadLine()之类的内容阅读您的信息流,并将其输出(由您的空格预先添加)写入TextWriter

答案 2 :(得分:0)

使用.Net XML类没有明显的方法可以做到这一点。 (例如,XmlTextWriter.Indent()不是虚拟的,如reference source所示。)您可能需要将XML流式传输到中间表示中,然后对其进行修补。但是,您可以使用来自CDATA nodes的Mark Fussell WriteShallowNode进行修补,而不是使用正则表达式进行修补(可能会导致Combining the XmlReader and XmlWriter classes for simple streaming transformations出现问题):

    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;

        }
    }
}