2

我正在修改.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否则它会更糟)。

4

7 回答 7

6

我不认为你可以用内置的XmlWriter实现来做到这一点......你可以从 XmlTextWriter 继承,并覆盖适当的方法(不确定它是哪一个......)以编写具有所需格式的元素


以下是一些支持 XML 的差异工具(将根据语义比较文件,忽略格式):

使用这些工具,您无需担心生成的 XML 格式

于 2009-08-17T16:50:51.170 回答
2

有关系吗?大概 .NET IDE 读取标准 XML。重要的是您的 XML 是合法的,而且显然是合法的。你真的有问题吗?

编辑:(另一个用户表示真正的问题是差异)。让我们调用您用来生成新结果 P 的任何过程,旧文件为 F。如果您运行 P(F),也就是说,只需读取 F 并将其写回而不做任何更改,您将获得新的 (不方便)格式的原始文件。

我猜你正在做的是运行 P(F+epsilon),你正在用 epsilon 更改修改原始 F,并生成它,然后你很难将新的与原始的进行比较。解决此问题的一种方法是简单地在原始文件上运行 P(F),并将其与 P(F+epsilon) 进行比较。现在大概两者的格式样式是相同的,并且您的差异将是合理的。这种特技被称为“标准化”。

另一种选择是运行一个能够理解 XML 的 diff 工具,从而知道什么格式是不相关的。

于 2009-09-01T08:03:18.867 回答
1

还有 WinMerge(免费,GPL),带有 xml 插件

于 2009-09-01T23:07:03.110 回答
1

也许改变你的差异工具会解决你的问题,因为其他一切显然运行良好。一些差异工具(例如WinMerge)可以选择过滤您想要忽略的差异,即使您可以提供正则表达式来定义规则

于 2009-09-03T13:03:25.837 回答
1

首先将其保存回 xml,使用以下方法的修改版本进行读取和重写,以注入换行符和制表符。为此,您可以使用 rdr.Depth 获取选项卡计数,并在调用 WriteEndElement) 之前使用 wtr.WriteRaw("\r\n" + new String('\t', tabCount)) 写入非显着的空白。抱歉,这是未经测试的代码,但它已尽可能接近您。

    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)
                );
        }
    }
于 2009-09-04T02:05:48.430 回答
1

XmlWriter.Create 返回包装在 XmlWellFormedWriter 中的特定 XmlRawWriter,所有这些都定义为内部的,因此您无法扩展它们。

但是,您可以扩展 XmlTextWriter,但与格式良好的编写器相比,它的功能集非常有限。

所以...

这是我为处理此问题而制作的自定义 XmlTextWriter,它具有格式良好的编写器所缺少的大部分功能,但 XmlWriterSettings 除外:

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 中的一个选项添加到格式良好的编写器中,那就太好了。

有时更改 diff 工具不是一个选项(甚至不是问题所在) 当使用像 perforce 这样的系统通过工具自动合并更改时,最好使更改尽可能原子化。

例如...如果最后一个属性被一个人更改,而另一个属性被另一个人添加到末尾,则没有理由根据哪一行应该包含结束 >(或 /> )。这种情况的最佳解决方案是,如果右括号在自己的行上,并且一起避免冲突。

于 2012-06-01T08:54:56.613 回答
0

听起来您正在修改很多文件,所以这可能不切实际,但是在 VS 中打开文件并保存它应该会恢复标准格式。

于 2009-09-04T14:51:38.937 回答