Come formattare un XML con chiusura nodi e i tag su una nuova linea?
-
18-09-2019 - |
Domanda
Sto modificando alcune .vcrpoj
i file in .NET, ma quando ho salvare le modifiche di formattazione (che i relitti caos con il mio diff strumento), i file originali assomigliare a questo:
<VisualStudioProject
ProjectType="Visual C++"
Version="8.00"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
Ma quando salvo le modifiche assomiglia a questo:
<VisualStudioProject
ProjectType="Visual C++"
Version="8.00">
<Platforms>
<Platform
Name="Win32" />
</Platforms>
<ToolFiles></ToolFiles>
Sto utilizzando il seguente XmlWritterSettings
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.IndentChars = ("\t");
settings.Encoding = Encoding.UTF8;
settings.NewLineOnAttributes = true;
C'è un modo per definire le impostazioni per abbinare il formato di visual studio utilizza?(Ho bisogno di NewLineOnAttributes
altrimenti è anche peggio).
Soluzione
Non credo che si può fare con il built-in XmlWriter
realizzazione di...si potrebbe ereditare da XmlTextWriter, e l'override del metodo appropriato (non è sicuro che quello che è...) per scrivere gli elementi con il formato desiderato
Qui ci sono alcune XML-conoscenza diff strumenti (che metterà a confronto i file in base alla semantica, ignorando la formattazione) :
- XML Diff e Patch Tool (libero, MS progetto di esempio, esiste la GUI e la riga di comando)
- Altova DiffDog (commerciale)
- XML Diff e Strumento di Unione (libero, piuttosto vecchio, ma probabilmente ancora funzionante)
- Al Di Là Di Confrontare (commerciale) con il formato "XML riordinato con gli attributi ordinati" (disponibile sul questa pagina)
Con questi strumenti, non avrete bisogno di preoccuparsi per il formato XML è possibile generare
Altri suggerimenti
Importa?Presumibilmente l' .NET IDE legge lo standard XML.Tutto ciò che conta è che il vostro XML è legale, e a quanto pare è.Hai davvero un problema?
EDIT:(Un altro utente indica il vero problema è con diff ing).Diciamo, indipendentemente dal processo che si sta utilizzando per produrre il nuovo risultato P, con il vecchio file F.Se si esegue P(F) che è, semplicemente leggi di F e scrivere di nuovo senza alcuna modifica, si otterrà il tuo nuovo (scomodo) formato di file originale.
Sto cercando di indovinare che cosa si sta facendo l'esecuzione di P(F+epsilon), dove si sta modificando l'originale F con epsilon modifiche, e la produzione di questo, e quindi si hanno difficoltà a confrontare il nuovo con l'originale.Un modo per risolvere questo problema, è semplicemente eseguire P(F) in originale, e confrontarlo con P(F+epsilon).Ora, presumibilmente, gli stili di formattazione di entrambi sono identici e i tuoi cambiamenti sarà ragionevole.Questo tipo di stunt è chiamato "normalizzazione".
L'altra alternativa è quella di eseguire un diff strumento che capisce XML, e quindi sa che cosa la formattazione è irrilevante.
Anche WinMerge (gratuito, GPL), con il plugin xml
Magari cambiando il diff strumento di risolvere i vostri problemi, come tutto il resto a quanto pare funziona bene.Alcuni diff strumenti come WinMerge hanno un opzione per filtrare quali sono le differenze che si desidera ignorare, anche è in grado di fornire una espressione regolare per definire la regola
Iniziare da saveing indietro xml, leggere e riscrivere con una versione modificata del metodo seguito per iniettare la line-feed e schede.Per fare questo, è possibile ottenere la scheda di conteggio utilizzando rdr.La profondità e l'uso wtr.WriteRaw(" " + new String(' ', tabCount)) appena prima di chiamare il WriteEndElement) per scrivere non significativo spazio bianco.Mi dispiace, questo è il codice non TESTATO, ma è il più vicino che posso ottenere.
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.Creare restituisce una specifica XmlRawWriter avvolto in un XmlWellFormedWriter, tutti definiti come interno in modo è possibile estendere la loro.
Tuttavia, è possibile estendere XmlTextWriter, ma è un insieme di funzionalità limitate rispetto alla ben formata scrittore.
Quindi...
Qui è un custom XmlTextWriter ho fatto per affrontare questo problema, che ha la maggior parte delle caratteristiche che mancano da ben formata scrittore, e stralci 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);
}
}
}
}
Basta fare un file contenente il codice di cui sopra il vostro progetto, e quindi utilizzarlo in questo modo:
// 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);
}
Sarebbe bello se questa funzionalità sono stati appena aggiunti a ben formata scrittore come opzione in XmlWriterSettings.
A volte cambiando la diff strumento non è un'opzione (o anche il problema in primo luogo) Quando si usano sistemi come necessariamente auto unire le modifiche tramite uno strumento, è meglio mantenere le modifiche atomic possibile.
Per esempio...Se l'ultimo attributo è cambiato di una sola persona, e un attributo aggiuntivo viene aggiunto alla fine di un'altra persona, non c'è motivo di creare un artificiale conflitto sulla base di una linea che dovrebbe contenere la chiusura > (o />).La soluzione migliore per questo scenario è se la parentesi di chiusura è sulla sua linea, e di evitare il conflitto, tutti insieme.
Suona come si sta modificando un sacco di file, quindi questo non può essere pratico, ma aprendo il file in VS e risparmio dovrebbe ripristinare la formattazione standard.