XDocument 在一行中写入特定的 XElement

Posted

技术标签:

【中文标题】XDocument 在一行中写入特定的 XElement【英文标题】:XDocument write specific XElement in a single line 【发布时间】:2019-07-31 09:41:23 【问题描述】:

我正在使用XDocument 编写一个 XML 文件。我想调整文件布局。让我解释一下,这里是生成文件的摘录:

<ROOTELEMENT>
  <CHILDELEMENT>
    <INFO1>Test a 1</INFO1>
    <INFO2>Test a 2</INFO2>
  </CHILDELEMENT>
  <CHILDELEMENT>
    <INFO1>Test b 1</INFO1>
    <INFO2>Test b 2</INFO2>
  </CHILDELEMENT>
<ROOTELEMENT>

我希望我的文件看起来像这样:

<ROOTELEMENT>
  <CHILDELEMENT><INFO1>Test a 1</INFO1><INFO2>Test a 2</INFO2></CHILDELEMENT>
  <CHILDELEMENT><INFO1>Test b 1</INFO1><INFO2>Test b 2</INFO2></CHILDELEMENT>
</ROOTELEMENT>

这是我的代码:

var myDoc = new XDocument(new XElement("ROOTELEMENT",
                                    new XElement("CHILDELEMENT",
                                        new XElement("INFO1", "Test a 1"),
                                        new XElement("INFO2", "Test a 2")),
                                    new XElement("CHILDELEMENT",
                                        new XElement("INFO1", "Test b 1"),
                                        new XElement("INFO2", "Test b 2"))));

myDoc.Save("Test.xml");

【问题讨论】:

真的很重要吗?看起来主要是格式问题 是的,这是格式问题。我正在替换用另一种语言编写的另一个应用程序,并且我希望输出与我的新应用程序出现之前完全相同。 我的意思是,XML 的缩进并不重要。任何半途而废的 XML 解析器都会忽略它,我什至不确定是否有一些东西没有 对我的客户来说,确实如此。我猜是为了节省内存并保持文件可读。 How do you create an indented XML string from an XDocument in c#?的可能重复 【参考方案1】:

XML 输出的格式由XmlWriter 而非XElementXDocument 控制,因此如果您需要精确控制格式,则需要通过继承XmlWriter 的实现之一来创建自己的编写器,特别是XmlTextWriterFormatting 属性是可变的,可以在写入期间更改。

例如,这是一个在元素深度超过 1 时禁用缩进的版本:

public class CustomFormattingXmlTextWriter : XmlTextWriter

    readonly Stack<Formatting> stack = new Stack<Formatting>();

    public CustomFormattingXmlTextWriter(TextWriter writer, int indentDepth) : base(writer) 
     
        this.Formatting = Formatting.Indented; 
        this.IndentDepth = indentDepth;
    
    
    int IndentDepth  get; 

    void OnElementStarted(string localName, string ns)
    
        stack.Push(Formatting);
        // You could e.g. modify the logic here to check to see whether localName == CHILDELEMENT
        // if (localName == "CHILDELEMENT")
        if (stack.Count == IndentDepth+1)
            Formatting = Formatting.None;
    

    void OnElementEnded()
    
        var old = stack.Pop();
        if (old != Formatting)
            Formatting = old;
    

    public override void WriteStartElement(string prefix, string localName, string ns)
    
        base.WriteStartElement(prefix, localName, ns);
        OnElementStarted(localName, ns);
    

    public override void WriteEndElement()
    
        base.WriteEndElement();
        OnElementEnded();
    
    
    public override void WriteFullEndElement()
    
        base.WriteEndElement();
        OnElementEnded();
    


public static partial class XNodeExtensions

    public static void SaveWithCustomFormatting(this XDocument doc, string filename, int indentDepth)
    
        using (var textWriter = new StreamWriter(filename))
        using (var writer = new CustomFormattingXmlTextWriter(textWriter, indentDepth))
        
            doc.Save(writer);
        
    

使用它,你可以做到:

myDoc.SaveWithCustomFormatting(fileName, 1);

根据需要输出:

<ROOTELEMENT>
  <CHILDELEMENT><INFO1>Test a 1</INFO1><INFO2>Test a 2</INFO2></CHILDELEMENT>
  <CHILDELEMENT><INFO1>Test b 1</INFO1><INFO2>Test b 2</INFO2></CHILDELEMENT>
</ROOTELEMENT>

注意事项:

您可以修改CustomFormattingXmlTextWriter.OnElementStarted() 中的逻辑,以使用您希望的任何标准禁用格式化,例如检查传入的localName 是否为CHILDELEMENT

XmlTextWriterXmlWriter 弃用——但后者没有可变的Formatting 属性。如果您必须使用XmlWriter,您可以查看Is there a way to serialize multiple XElements onto the same line?

演示小提琴 #1 here.

【讨论】:

以上是关于XDocument 在一行中写入特定的 XElement的主要内容,如果未能解决你的问题,请参考以下文章

在 XDocument 中编辑特定元素

c# XDocument:检查特定节点名称是不是存在,如果不添加

强制 XDocument 使用 UTF-8 编码写入字符串

VB 写入文件写入特定一行

在 XDocument 中按名称查询任意深度的元素

在 XDocument 中搜索 XML 节点时避免 Try n Catch