2009-08-17 49 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這樣的系統通過工具自動合併更改時,最好儘可能保持更改原子。

例如... 如果最後一個屬性被一個人改變,並且另一個人被添加到另一個屬性的末尾,沒有理由根據哪一行應該包含關閉來創建人爲衝突>(或/>)。 對於這種情況,最好的解決方案是如果右括號是在自己的行上,並且避免所有的衝突。