C# 使用override void WriteEndElement()时如何使用XmlWriterSettings()?

C# 使用override void WriteEndElement()时如何使用XmlWriterSettings()?,c#,xml,serialization,C#,Xml,Serialization,我使用的是一个遗留应用程序,它不导入缩写的空xml元素。例如: 坏空: <foo /> 和客户端代码: var x_settings = new XmlWriterSettings(); x_settings.NewLineChars = Environment.NewLine; x_settings.NewLineOnAttributes = true;

我使用的是一个遗留应用程序,它不导入缩写的空xml元素。例如:

坏空:

<foo />
和客户端代码:

                    var x_settings = new XmlWriterSettings();
                    x_settings.NewLineChars = Environment.NewLine;
                    x_settings.NewLineOnAttributes = true;
                    x_settings.NewLineHandling = NewLineHandling.Replace;
                    x_settings.CloseOutput = true;
                    x_settings.Indent = true;
                    x_settings.NewLineOnAttributes = true;

                    //var memOut = new MemoryStream();
                    var writer = new XmlTextWriterFull(outputFilename, Encoding.UTF8); //Or the encoding of your choice
                    var x_serial = new XmlSerializer(typeof(YOUR_OBJECT_TYPE));
                    x_serial.Serialize(writer, YOUR_OBJECT_INSTANCE);

                    writer.Close();
但是,如果仔细观察,客户机代码中永远不会使用
XmlWriterSettings
。因此,xml输出的格式非常糟糕。我的问题是:如何调整上述代码以接受
XmlWriterSettings

工厂创建方法和密封/内部/抽象类的使用使得实现重写变得困难

我将接受另一种解决方案,我不同意我的上述解决方案

  • 解决方案
步骤1:在解决方案中创建以下类:

public class XmlTextWriterFull : XmlTextWriter
{
    public XmlTextWriterFull(TextWriter sink) : base(sink)
    {
        Formatting = Formatting.Indented;
    }

    public override void WriteEndElement()
    {
        base.WriteFullEndElement();
    }
}
步骤2:添加以下客户端代码。确保使用您正在使用的类和实例替换您的\u对象\u类型和\u对象\u实例:

TextWriter streamWriter = new StreamWriter(outputFilename);
var writer = new XmlTextWriterFull(streamWriter);

var x_serial = new XmlSerializer(typeof (YOUR_OBJECT_TYPE));
x_serial.Serialize(writer, YOUR_OBJECT_INSTANCE);

writer.Close();
上述解决方法将生成以下空xml元素格式:

<foo>
</foo>

此解决方案的问题在于它添加了换行符(请注意,元素位于单独的行上)。这对您来说可能是可以接受的,但会导致我的旧版应用程序出现问题。

如何

从中获取可怕的
XmlWrappingWriter
类(为了简洁起见,我省略了代码)

这样,我们可以创建一个子类,如下所示(非常类似于您原来的子类):

然后可以这样调用它(同样非常类似):

在我的例子中,一个以前被渲染为

<Foo>
</Foo>
可用于将
XmlWriter设置应用于
XmlWriter
。我无法让它像我所希望的那样工作(例如,缩进从未工作过),并且在反编译代码时,似乎并不是所有的设置都与这个特定的方法一起使用,因此我的调用代码只是在
输出文件中传递(某种类型的流也可以工作)。

另一个选项

public class XmlCustomTextWriter : XmlTextWriter
{
    private TextWriter _tw = null;

    public XmlCustomTextWriter(TextWriter sink)
        : base(sink)
    {
        _tw = sink;
        Formatting = Formatting.Indented;
        Indentation = 0;
    }

    public void OutputElement(string name, string value)
    {
        WriteStartElement(name);
        string nl = _tw.NewLine;
        _tw.NewLine = "";
        WriteString(value);
        WriteFullEndElement();
        _tw.NewLine = nl;
    }
}

您在问题中给出的变通解决方案添加了额外的换行符(启用缩进时),因为我们告诉编写者将此元素视为具有子元素

下面是我如何修改您的解决方法来动态操作缩进,以避免那些额外的换行

public class XmlTextWriterFull : XmlTextWriter
{
    public XmlTextWriterFull(TextWriter sink)
        : base(sink)
    {
        Formatting = Formatting.Indented;
    }

    private bool inElement = false;

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

        // Remember that we're in the process of defining an element.
        // As soon as a child element is closed, this flag won't be true anymore and we'll know to avoid messing with the indenting.
        this.inElement = true;
    }

    public override void WriteEndElement()
    {
        if (!this.inElement)
        {
            // The element being closed has child elements, so we should just let the writer use it's default behavior.
            base.WriteEndElement();
        }
        else
        {
            // It looks like the element doesn't have children, and we want to avoid emitting a self-closing tag.
            // First, let's temporarily disable any indenting, then force the full closing element tag.
            var prevFormat = this.Formatting;
            this.Formatting = Formatting.None;
            base.WriteFullEndElement();
            this.Formatting = prevFormat;
            this.inElement = false;
        }
    }
}

把这个留在这里,以防有人需要它;因为上面的答案没有一个能帮我解决这个问题,或者看起来有点过分了

    FileStream fs = new FileStream("file.xml", FileMode.Create);
    XmlWriterSettings settings = new XmlWriterSettings();
    settings.Indent = true;
    XmlWriter w = XmlWriter.Create(fs, settings);
    w.WriteStartDocument();
    w.WriteStartElement("tag1");

        w.WriteStartElement("tag2");
        w.WriteAttributeString("attr1", "val1");
        w.WriteAttributeString("attr2", "val2");
        w.WriteFullEndElement();

    w.WriteEndElement();
    w.WriteEndDocument();
    w.Flush();
    fs.Close();
诀窍是设置XmlWriterSettings.Indent=true并将其添加到XmlWriter

编辑: 或者,您也可以使用

w.Formatting = Formatting.Indented;

不要添加XmlWriterSettings。

下面的代码片段强制在同一行上打印结束标记(很抱歉,对于vb版本,使用C#重写相同的标记应该很容易):


不知道它是否解决了这个特殊情况,但您可以使用XmlWriter.Create重载,该重载接受现有的writer和设置。您可以将自己的writer实现传递给该方法。据我所知,它允许您“向底层XmlWriter对象添加其他功能”,但我不知道这是否包括格式设置。我无法回答自己的问题。但我确实找到了一个可以接受的解决办法。我会把它贴在这里,希望能帮助别人;然而,我仍然希望我的问题得到解决。我的变通方法不使用我想要的XmlWriterSettings。更新:我的旧版应用程序不接受上述变通方法,因为完整标记插入了换行元素;因此,标记显示在单独的行上。我发布了一个悬赏,想看看我是否能吸引更多的注意力。这有点蹩脚,但你可以输出后处理你的输出,并用代码替换
\s+你有机会看看我的答案吗?我很有信心它会解决你的问题。
<Foo></Foo>
XmlWriter.Create(XmlWriter, XmlWriterSettings)
public class XmlCustomTextWriter : XmlTextWriter
{
    private TextWriter _tw = null;

    public XmlCustomTextWriter(TextWriter sink)
        : base(sink)
    {
        _tw = sink;
        Formatting = Formatting.Indented;
        Indentation = 0;
    }

    public void OutputElement(string name, string value)
    {
        WriteStartElement(name);
        string nl = _tw.NewLine;
        _tw.NewLine = "";
        WriteString(value);
        WriteFullEndElement();
        _tw.NewLine = nl;
    }
}
public class XmlTextWriterFull : XmlTextWriter
{
    public XmlTextWriterFull(TextWriter sink)
        : base(sink)
    {
        Formatting = Formatting.Indented;
    }

    private bool inElement = false;

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

        // Remember that we're in the process of defining an element.
        // As soon as a child element is closed, this flag won't be true anymore and we'll know to avoid messing with the indenting.
        this.inElement = true;
    }

    public override void WriteEndElement()
    {
        if (!this.inElement)
        {
            // The element being closed has child elements, so we should just let the writer use it's default behavior.
            base.WriteEndElement();
        }
        else
        {
            // It looks like the element doesn't have children, and we want to avoid emitting a self-closing tag.
            // First, let's temporarily disable any indenting, then force the full closing element tag.
            var prevFormat = this.Formatting;
            this.Formatting = Formatting.None;
            base.WriteFullEndElement();
            this.Formatting = prevFormat;
            this.inElement = false;
        }
    }
}
    FileStream fs = new FileStream("file.xml", FileMode.Create);
    XmlWriterSettings settings = new XmlWriterSettings();
    settings.Indent = true;
    XmlWriter w = XmlWriter.Create(fs, settings);
    w.WriteStartDocument();
    w.WriteStartElement("tag1");

        w.WriteStartElement("tag2");
        w.WriteAttributeString("attr1", "val1");
        w.WriteAttributeString("attr2", "val2");
        w.WriteFullEndElement();

    w.WriteEndElement();
    w.WriteEndDocument();
    w.Flush();
    fs.Close();
w.Formatting = Formatting.Indented;
Imports System.Xml
Imports System.IO
Public Class CustomXmlTextWriter
    Inherits XmlTextWriter

    Public Sub New(ByRef baseWriter As TextWriter)
        MyBase.New(baseWriter)
        Formatting = Xml.Formatting.Indented
    End Sub

    Public Overrides Sub WriteEndElement()
        If Not (Me.WriteState = Xml.WriteState.Element) Then
            MyBase.WriteEndElement()
        Else
            Formatting = Xml.Formatting.None
            MyBase.WriteFullEndElement()
            Formatting = Xml.Formatting.Indented
        End If
    End Sub

End Class