【问题标题】:How to use XmlWriterSettings() when using override void WriteEndElement()?使用 override void WriteEndElement() 时如何使用 XmlWriterSettings()?
【发布时间】:2012-11-15 00:49:14
【问题描述】:

我正在使用不导入缩写的空 xml 元素的旧版应用程序。例如:

坏空:

<foo />

好空:

<foo></foo>

我知道实现这一点的解决方案,我现在将介绍它:

public class XmlTextWriterFull : XmlTextWriter
{


    public XmlTextWriterFull(Stream stream, Encoding enc) : base(stream, enc)
    {
    }

    public XmlTextWriterFull(String str, Encoding enc) : base(str, enc) 
    {
    }

    public override void WriteEndElement()
    {
        base.WriteFullEndElement();
    }
}

和客户端代码:

                    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 步:添加以下客户端代码。确保将 YOUR_OBJECT_TYPE 和 YOUR_OBJECT_INSTANCE 替换为您正在使用的类和实例:

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>

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

【问题讨论】:

  • 不知道它是否解决了这种特殊情况,但您可以使用 XmlWriter.Create 重载,它采用现有的编写器和设置。您可以将自己的编写器实现传递给该方法。根据msdn.microsoft.com/en-us/library/a09119h4.aspx,它允许您“向底层 XmlWriter 对象添加附加功能”,但我不知道这是否包括格式。
  • 我无法回答我自己的问题。但我确实找到了一个可以接受的解决方法。我会把它贴在这里,希望能帮助别人;但是,我仍然想要解决我的问题。我的解决方法不使用我想要的 XmlWriterSettings。
  • 更新:我的旧应用程序不接受上述解决方法,因为完整标签 插入了换行元素;因此标签出现在不同的行上。我正在发布一个赏金,看看我是否可以引起更多的关注。
  • 这有点蹩脚和hacky,但你可以输出后处理你的输出并将&gt;\s+&lt;/替换为&gt;&lt;/
  • 你有机会看看我的回答吗?我相当有信心它会解决您的问题。

标签: c# xml serialization


【解决方案1】:

这个怎么样。

http://www.tkachenko.com/blog/archives/000585.html 中获取很棒的XmlWrappingWriter 类(为简洁起见,我省略了代码)。

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

public class XmlTextWriterFull2 : XmlWrappingWriter
{
    public XmlTextWriterFull2(XmlWriter baseWriter)
        : base(baseWriter)
    {
    }

    public override void WriteEndElement()
    {
        base.WriteFullEndElement();
    }
}

然后可以像这样调用它(再次非常相似):

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

using (XmlWriter writer = XmlWriter.Create(outputFilename, x_settings))
{
    using (XmlTextWriterFull2 xmlTextWriterFull = new XmlTextWriterFull2(writer))
    {
        var x_serial = new XmlSerializer(typeof(YOUR_OBJECT_TYPE));
        x_serial.Serialize(xmlTextWriterFull, YOUR_OBJECT_INSTANCE);
    }
}

在我的例子中,一个元素之前被渲染为

<Foo>
</Foo>

成为

<Foo></Foo>

正如您在问题中提到的那样,这实际上是一个非常棘手的问题,因为一切都被密封/内部等,使得覆盖相当困难。我认为我最大的问题是试图让XmlWriter 接受XmlWriterSettings:除了这种方法,我找不到让原始XmlTextWriterFull 尊重给定XmlWriterSettings 的方法。

MSDN 声明这个方法:

XmlWriter.Create(XmlWriter, XmlWriterSettings)

可用于将XmlWriterSettings 应用于XmlWriter。我无法让它像我想要的那样工作(例如,缩进从来没有工作过),并且在反编译代码时,似乎所有设置都与这个特定方法一起使用,因此为什么我的调用代码只是传入outputFile(某种流也可以工作)。

【讨论】:

    【解决方案2】:

    您在问题中提供的解决方法添加了额外的换行符(启用缩进时),因为我们告诉作者将此元素视为有子元素。

    这是我如何修改您的解决方法以动态操作缩进以避免那些额外的换行符。

    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;
            }
        }
    }
    

    【讨论】:

      【解决方案3】:

      以下代码 sn -p 强制在同一行打印结束标记(对不起 vb 版本,使用 C# 应该很容易重写):

      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
      

      【讨论】:

        【解决方案4】:

        另一种选择。

        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;
            }
        }
        

        【讨论】:

          【解决方案5】:

          把它留在这里以防有人需要它;因为上面的答案都没有为我解决这个问题,或者看起来有点矫枉过正。

              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。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2018-07-28
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多