2009-08-17 51 views
2

我修改了一些.vcrpoj文件.NET但是当我保存的格式更改(其中沉船严重破坏了我的比较工具),原始文件是这样的:如何在新行上使用关闭节点和标签来格式化XML?

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

有没有办法将t定义他设置为了匹配视觉工作室使用的格式? (我需要NewLineOnAttributes,否则会更糟糕)。

回答

6

我不认为你可以用内置的XmlWriter实现它......你可以继承自XmlTextWriter,并重写适当的方法(不知道它是哪一个...)来写元素所需的格式


这里有一些XML的差异工具(将比较基于语义的文件,忽略格式):

随着这些工具,你将不必担心XML格式您生成

+0

你能更具体?当从'继承XmlWriter'我甚至不看如何构建我的'base'(带内置的执行我使用静态'.Create'方法,但我怎么通过这个我'base'工作时?)。 – Motti 2009-08-23 14:48:42

+0

XmlWriter构造函数受保护,因此您可以从派生类中调用它。然而,它会更容易直接从XmlTextWriter的继承,这样你就不必编写一切从头开始 – 2009-08-23 14:56:53

+2

我给它一个尝试,它似乎并不可能做到,如果没有大量的代码......反正,如果你唯一的格式问题是差异工具,我建议你使用一种理解XML的工具,并比较语义,而不仅仅是文本。如果您使用Beyond Compare,则在比较它们之前,会有一个插件用Tidy格式化这两个文档。 – 2009-08-23 15:55:51

2

什么关系呢?据推测,.NET IDE读取标准XML。 所有重要的是,你的XML是合法的,而且很明显。 你真的有问题吗?

编辑:(另一个用户指示真正的问题是与diff'ing)。 我们叫什么过程中,你正在使用,以产生新的结果 P,与旧的文件是F.如果运行P(F)也就是简单地 阅读F和它写回没有任何变化,你会得到 原始文件上的新(不便)格式。

我猜你在做什么 运行P(F +小量),你在哪里用小量的变化修改原楼 和生产这一点,那么你必须与原来相比,新 困难。解决这个问题的一种方法是简单地在原始上运行P(F) ,并将其与P(F +ε)进行比较。 现在想必格式样式两者是相同的, 您的diff将是合理的。这种特技被称为 “正常化”。

另一种方法是运行一个diff工具理解XML, 从而知道什么格式是无关紧要的。

+0

再次阅读该问题:“当我将它们保存为格式更改(**,这会破坏我的差异工具**)” – 2009-09-01 08:14:40

+0

+1 for XML aware diff tool – 2009-09-02 15:54:25

1

另外的WinMerge(免费,GPL),与XML插件

1

可能改变你的比较工具将解决您的问题,因为一切似乎运行正常。一些比较工具如WinMerge可以选择过滤要忽略的差异,甚至可以提供正则表达式来定义规则

1

首先将其保存回xml,然后使用该方法的修改版本进行读取和重写在下面注入换行符和制表符。要做到这一点,你可以用rdr.Depth得到的标签数量和使用wtr.WriteRaw(“\ r \ n” +新的String(“\ t”,tabCount))调用对writeEndElement之前)写的非显着的空白空间。对不起,这是UNTESTED代码,但它尽可能地接近我。

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

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

1

XmlWriter.Create返回特定XmlRawWriter包裹在XmlWellFormedWriter,所有这些被定义为内部所以不能扩展它们。

但是,您可以扩展XmlTextWriter的,但相对于中规中矩的作家它有一个非常有限的功能集。

所以......

这是我作出处理这个问题上,有多数的功能从井中形成的作家失踪,节选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); 
     } 

这将是很好,如果这个功能刚刚加入到孔中形成的WR iter作为XmlWriterSettings中的一个选项。

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

例如... 如果最后一个属性被一个人改变,并且另一个人被添加到另一个属性的末尾,没有理由根据哪一行应该包含关闭来创建人为冲突>(或/>)。 对于这种情况,最好的解决方案是如果右括号是在自己的行上,并且避免所有的冲突。