終了ノードとタグを新しい行に配置して XML をフォーマットするにはどうすればよいですか?

StackOverflow https://stackoverflow.com/questions/1288125

質問

一部修正してます .vcrpoj ファイルは .NET にありますが、書式変更を保存すると (diff ツールでは大混乱が生じます)、元のファイルは次のようになります。

<VisualStudioProject
ProjectType="Visual C++"
Version="8.00"
>
<Platforms>
    <Platform
        Name="Win32"
    />
</Platforms>
<ToolFiles>
</ToolFiles>

しかし、変更を保存すると次のようになります。

<VisualStudioProject
ProjectType="Visual C++"
Version="8.00">
<Platforms>
    <Platform
        Name="Win32" />
</Platforms>
<ToolFiles></ToolFiles>

私は以下を使用しています XmlWritterSettings

XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.IndentChars = ("\t");
settings.Encoding = Encoding.UTF8;
settings.NewLineOnAttributes = true;

Visual Studio が使用する形式に一致するように設定を定義する方法はありますか?(私は欲しい NewLineOnAttributes そうでなければ、さらに悪いことになります)。

役に立ちましたか?

解決

内蔵ではできないと思います XmlWriter 実装...XmlTextWriter から継承し、適切なメソッド (どれであるかはわかりません...) をオーバーライドして、目的の形式で要素を書き込むことができます。


以下に、XML 対応の差分ツールをいくつか示します (書式設定を無視して、セマンティクスに基づいてファイルを比較します)。

これらのツールを使用すると、生成する XML 形式について心配する必要がなくなります。

他のヒント

これは重要ですか?おそらく.NET IDEには、標準のXMLを読み込みます。 カウントするのはあなたのXMLが合法であるということである、とapparantlyそれがあります。 あなたが本当に問題を持っていますか?

EDIT:(別のユーザーが、本当の問題はdiff'ingであることを示します)。 あなたはあなたの新しい結果を生成するために使用しているどんなプロセス呼ぶことにしましょう P、であるあなたがP(F)を実行した場合F.である古いファイルと、単に Fを読み、何も変更せずに外に書き戻し、あなたが買ってあげます 元のファイルに新しい(不便)形式ます。

私はあなたが何をしているかを推測しています あなたはイプシロン変更で元Fを変更しているP(Fの+イプシロン)を実行しています、 これを生産し、その後、あなたは困難な新しい比較を持っています オリジナルと。この問題を解決する1つの方法は、単純にあります 原稿上のP(F)を実行し、P(F + 1イプシロン)と比較。 今、おそらく両方の書式設定スタイルが同一であり、 あなたの差分が合理的になります。スタントのこの種と呼ばれています "正規化" ます。

他の代替は、XMLを理解して差分ツールを実行することです、 したがって、フォーマットが無関係であることを知っている。

またWinMergeの(無料、GPL)、XMLプラグインと

たぶん、他のすべてのように、あなたの問題を解決しますあなたの差分ツールを変更するには、明らかに細かい動作します。 WinMergeののようないくつかのdiffツールも、あなたは定期的に供給することができ、あなたが無視したいものの違いフィルタリングするためのオプションがあります

ルールを定義する式

バックXMLにそれをsaveingから始め、読み、ラインフィードとタブを注入するために、次の方法の修飾バージョンで書き換えます。これを行うには、あなたがrdr.Depthを使用して、タブの数を取得し、wtr.WriteRawを使用することができます(「\ rは\ n」は+新しい文字列(「\ tの」、tabCount))あなたはWriteEndElementを呼び出す直前には)非を書き込みます重要な空白。申し訳ありませんが、これは未テストコードですが、私があなたを得ることが可能な限り近います。

    public void CopyXmlContentsToFile(XmlTextReader rdr, XmlTextWriter wtr)
    {
        try
        {
            rdr.WhitespaceHandling = WhitespaceHandling.Significant;
            wtr.Formatting = Formatting.Indented;
            CopyNodes(rdr, wtr);
        }
        finally
        {
            rdr.Close();
            wtr.Close();
        }
    }


    void CopyNodes(XmlReader rdr, XmlWriter wtr)
    {
        if (rdr.NodeType == XmlNodeType.Text || rdr.NodeType == XmlNodeType.SignificantWhitespace)
        {
            wtr.WriteString(rdr.Value);
        }
        else if (rdr.NodeType == XmlNodeType.Whitespace)
            return;
        else if (rdr.NodeType == XmlNodeType.Element)
        {
            string elemName = rdr.LocalName;
            bool empty = rdr.IsEmptyElement;

            wtr.WriteStartElement(elemName);

            while (rdr.MoveToNextAttribute())
            {
                if (rdr.Prefix.Length == 0)
                    wtr.WriteAttributeString(rdr.LocalName, rdr.Value);
            }

            if (rdr.NodeType == XmlNodeType.Attribute)
                rdr.MoveToElement();

            if (!empty)
            {
                while (rdr.Read() && rdr.NodeType != XmlNodeType.EndElement)
                    CopyNodes(rdr, wtr);
            }

            if (!empty && wtr.WriteState != WriteState.Content)
                wtr.WriteRaw("");

            if (!empty)
            {
                //Here we can inject our custom formatting with WriteRaw():
                wtr.WriteRaw(Environment.NewLine + new String('\t', rdr.Depth));
            }

            wtr.WriteEndElement();
        }
        else
        {
            throw new ApplicationException(
                String.Format("Unexpected node type {0} at line {1}.", rdr.NodeType, ((XmlTextReader)rdr).LineNumber)
                );
        }
    }

XmlWriter.Createは、あなたがそれらを拡張することはできませんので、内部のように定義されているすべてのそれらのXmlWellFormedWriterに包まれた特定のXmlRawWriterを返します。

しかし、あなたがてXmlTextWriterを拡張することができますが、それがうまく形成されライターに比べて非常に限られた機能セットを持っています。

だから... ...

ここで私がうまく形成さライタから欠けている機能、およびexceptsでのXmlWriterSettingsのほとんどを持ってこの問題に対処するために作られたカスタムてXmlTextWriterはあります:

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;

namespace System.Xml
{
    public class CustomXmlTextWriter : XmlTextWriter
    {
        internal class CustomStreamWriter : StreamWriter
        {
            public CustomStreamWriter(Stream stream, Encoding encoding) : base(stream) { }
            // This prevents the XmlTextWriter from writing the extra space before attributes, and the short EndElement " />"
            public bool DisableSpace { get; set; }
            public override void Write(char value)
            {
                if (DisableSpace && value == ' ') return;
                else base.Write(value);
            }
            public override void Write(string value)
            {
                if (DisableSpace && value == " /") base.Write('/');
                else base.Write(value);
            }
        }

        public CustomXmlTextWriter(string filename, XmlWriterSettings settings) : this(new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.Read), settings) { }
        public CustomXmlTextWriter(Stream stream, XmlWriterSettings settings) : this(new CustomStreamWriter(stream, settings.Encoding), settings) { }
        internal CustomXmlTextWriter(CustomStreamWriter writer, XmlWriterSettings settings)
            : base(writer)
        {
            m_Writer = writer;
            m_Settings = settings;

            if (m_Settings.OmitXmlDeclaration == false)
            {
                string encoding = (m_Writer.Encoding.CodePage == 1201) ? "UTF-16BE" : m_Writer.Encoding.WebName;
                m_Writer.WriteLine("<?xml version=\"1.0\" encoding=\"{0}\"?>", encoding);
            }
        }

        private bool m_HasAttributes = false;
        private Stack<bool> m_HasAttributesStack = new Stack<bool>();
        private CustomStreamWriter m_Writer;
        private XmlWriterSettings m_Settings;

        public override XmlWriterSettings Settings { get { return m_Settings; } }

        public override void WriteStartElement(string prefix, string localName, string ns)
        {
            if (WriteState == WriteState.Element)
            {
                if (m_HasAttributes && Settings.NewLineOnAttributes) { WriteIndent(m_HasAttributesStack.Count); }
                WriteRaw(""); // Trick the XmlTextWriter into closing the previous element, and updating the WriteState
                m_Writer.DisableSpace = false;
            }
            int indentLevel = m_HasAttributesStack.Count;
            if (indentLevel > 0)
            {
                WriteIndent(indentLevel);
            }
            m_HasAttributesStack.Push(m_HasAttributes);
            m_HasAttributes = false;

            base.WriteStartElement(prefix, localName, ns);
        }

        public override void WriteEndElement()
        {
            if (m_HasAttributes && Settings.NewLineOnAttributes)
            {
                WriteIndent(m_HasAttributesStack.Count - 1);
            }
            m_HasAttributes = m_HasAttributesStack.Pop();
            base.WriteEndElement();

            m_Writer.DisableSpace = false;
        }

        public override void WriteFullEndElement()
        {
            m_HasAttributes = m_HasAttributesStack.Pop();
            WriteIndent(m_HasAttributesStack.Count);
            base.WriteFullEndElement();
        }

        public override void WriteStartAttribute(string prefix, string localName, string ns)
        {
            if (Settings.NewLineOnAttributes)
            {
                WriteIndent(m_HasAttributesStack.Count);
                m_Writer.DisableSpace = true;
            }
            m_HasAttributes = true;
            base.WriteStartAttribute(prefix, localName, ns);
        }

        public override void WriteString(string text)
        {
            if (m_Settings.NewLineHandling == NewLineHandling.Replace)
            {
                text = Regex.Replace(text, @"\r\n?|\n", m_Settings.NewLineChars);
            }
            else if (m_Settings.NewLineHandling == NewLineHandling.Entitize)
            {
                text = Regex.Replace(text, @"\n|\r", m => String.Format("&#x{0:X};", (int)m.Value[0]));
            }
            base.WriteString(text);
        }

        private void WriteIndent(int indentLevel)
        {
            if (Settings.Indent == false) return;
            m_Writer.Write(Settings.NewLineChars);
            for (int i = 0; i < indentLevel; ++i)
            {
                m_Writer.Write(Settings.IndentChars);
            }
        }
    }
}

ちょうどあなたのプロジェクトに上記のコードを含むファイルを作成し、次にそのようにそれを使用します:

        // Create the XmlWriter Settings as you normally would
        // *Note: You can change or omit these, they are just for an example of what I supported
        XmlWriterSettings settings = new XmlWriterSettings()
        {
            Encoding = Encoding.UTF8,
            //OmitXmlDeclaration = true,
            Indent = true,
            //IndentChars = "  ",
            IndentChars = "\t",
            NewLineOnAttributes = true,
            //NewLineHandling = NewLineHandling.Entitize,
            //NewLineHandling = NewLineHandling.Replace,
            //NewLineChars = @"\n",
        };

        // Replace XmlWriter.Create with new CustomXmlTextWriter
        //using (XmlWriter writer = XmlWriter.Create(path, settings))
        using (XmlWriter writer = new CustomXmlTextWriter(path, settings))
        {
            xml.WriteTo(writer);
        }
この機能は、単にXmlWriterSettingsのオプションとしても形成されたライターに追加された場合です。

それはいいだろう

時々、差分ツールは、オプション(または最初の場所でさえ問題)ではありません変更 ツールを経由して自動マージ変化にPERFORCEのようなシステムを使用している場合、それは可能な限り、原子として変更を保持するのが最善です。

たとえば... 最後の属性は、一人で変更され、追加の属性が別の人によって最後に追加されている場合は、>終了を含めるべきラインに基づいて人工的な対立を作成する理由がない(または/>)。 このシナリオのための最善の解決策は、閉じ括弧は独自の行にある場合であり、すべて一緒に競合を避けるます。

それはあなたがたくさんのファイルを修正しているので、これは実用的ではないかもしれませんが、VSでファイルを開くと、それは、標準的な書式設定を復元する必要があります保存するように聞こえるます。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top