10

省略された空の 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>

この回避策の問題は、改行が追加されることです (要素が別々の行にあることに注意してください)。これは受け入れられるかもしれませんが、レガシー アプリケーションで問題が発生します。

4

5 に答える 5

5

これはどう。

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ます。XmlTextWriterFullXmlWriterSettings

MSDNは、この方法について次のように述べています。

XmlWriter.Create(XmlWriter, XmlWriterSettings)

XmlWriterSettingsを に適用するために使用できますXmlWriter。これを希望どおりに機能させることができませんでした (たとえば、インデントが機能しませんでした)。コードを逆コンパイルすると、すべての設定がこの特定のメソッドで使用されているようには見えません。outputFile(ある種のストリームも同様に機能します) 。

于 2012-11-20T10:18:54.913 に答える
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;
        }
    }
}
于 2014-12-05T02:58:36.163 に答える
1

次のコード スニペットは、同じ行に終了タグを強制的に出力します (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
于 2016-04-19T17:19:22.797 に答える
0

誰かがそれを必要とする場合に備えて、これをここに残しておきます。上記の答えのどれも私のためにそれを解決しなかったか、やり過ぎのように見えたので.

    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 を追加する代わりに。

于 2015-09-09T08:00:12.817 に答える
0

別のオプション。

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;
    }
}
于 2013-02-15T12:58:43.627 に答える