2012-11-15 14 views
8

Sto lavorando con un'applicazione legacy che non importa elementi xml vuoti abbreviati. Ad esempio:Come utilizzare XmlWriterSettings() quando si utilizza l'override void WriteEndElement()?

BAD vuoto:

<foo /> 

BUONA vuoto:

<foo></foo> 

So che la soluzione per ottenere questo, che presenterò ora:

public class XmlTextWriterFull : XmlTextWriter 
{ 


    public XmlTextWriterFull(Stream stream, Encoding enc) : base(stream, enc) 
    { 
    } 

    public XmlTextWriterFull(String str, Encoding enc) : base(str, enc) 
    { 
    } 

    public override void WriteEndElement() 
    { 
     base.WriteFullEndElement(); 
    } 
} 

e la codice cliente:

    var x_settings = new XmlWriterSettings(); 
        x_settings.NewLineChars = Environment.NewLine; 
        x_settings.NewLineOnAttributes = true; 
        x_settings.NewLineHandling = NewLineHandling.Replace; 
        x_settings.CloseOutput = true; 
        x_settings.Indent = true; 
        x_settings.NewLineOnAttributes = true; 

        //var memOut = new MemoryStream(); 
        var writer = new XmlTextWriterFull(outputFilename, Encoding.UTF8); //Or the encoding of your choice 
        var x_serial = new XmlSerializer(typeof(YOUR_OBJECT_TYPE)); 
        x_serial.Serialize(writer, YOUR_OBJECT_INSTANCE); 

        writer.Close(); 

Tuttavia, se si osservano attentamente, i codici XmlWriterSettings non vengono mai utilizzati nel codice client. Pertanto l'output xml è terribilmente formattato. Le mie domande sono queste: come posso adattare il codice sopra per accettare XmlWriterSettings?

L'utilizzo di metodi di creazione di fabbrica e classi sigillate/interne/astratte rende difficile l'implementazione di un override.

Accetterò una soluzione alternativa, non sono sposato con la mia soluzione di cui sopra.

  • SOLUZIONE SOLUZIONE

Fase 1: creare la seguente classe nella soluzione:

public class XmlTextWriterFull : XmlTextWriter 
{ 
    public XmlTextWriterFull(TextWriter sink) : base(sink) 
    { 
     Formatting = Formatting.Indented; 
    } 

    public override void WriteEndElement() 
    { 
     base.WriteFullEndElement(); 
    } 
} 

Fase 2: Aggiungere il seguente codice client. Assicurarsi di sostituire YOUR_OBJECT_TYPE e YOUR_OBJECT_INSTANCE con la classe e l'istanza si sta lavorando con:

TextWriter streamWriter = new StreamWriter(outputFilename); 
var writer = new XmlTextWriterFull(streamWriter); 

var x_serial = new XmlSerializer(typeof (YOUR_OBJECT_TYPE)); 
x_serial.Serialize(writer, YOUR_OBJECT_INSTANCE); 

writer.Close(); 

la soluzione precedente produrrà il seguente elemento vuoto xml formattazione:

<foo> 
</foo> 

Il problema con questa soluzione è che aggiunge un avanzamento riga (nota che gli elementi sono su righe separate). Questo potrebbe essere accettabile per te ma causa problemi con la mia applicazione legacy.

+0

Non so se affronta questo caso particolare , ma è possibile utilizzare il sovraccarico XmlWriter.Create che accetta uno scrittore esistente e le impostazioni. È possibile passare l'implementazione del proprio autore a quel metodo. Secondo http://msdn.microsoft.com/en-us/library/a09119h4.aspx, consente di "aggiungere funzionalità aggiuntive a un oggetto XmlWriter sottostante", ma non so se questo include la formattazione. – fsimonazzi

+0

Non sono stato in grado di rispondere alla mia stessa domanda. Ma ho trovato una soluzione accettabile. Lo posterò qui per aiutare eventualmente qualcun altro; tuttavia, voglio ancora una soluzione alla mia domanda. La mia soluzione alternativa non utilizza XmlWriterSettings che è ciò che voglio. Aggiornamento – sapbucket

+0

: la mia applicazione legacy non accetta la soluzione precedente perché il tag completo ha inserito un elemento di avanzamento riga; quindi i tag appaiono su linee separate. Sto pubblicando una taglia per vedere se posso attirare più attenzione alla domanda. – sapbucket

risposta

4

Che ne dici di questo.

Afferra il fantastico XmlWrappingWriter classe da http://www.tkachenko.com/blog/archives/000585.html (ho omesso il codice per brevità).

Con questo, siamo in grado di creare una sottoclasse come segue (molto simile a quella originale):

public class XmlTextWriterFull2 : XmlWrappingWriter 
{ 
    public XmlTextWriterFull2(XmlWriter baseWriter) 
     : base(baseWriter) 
    { 
    } 

    public override void WriteEndElement() 
    { 
     base.WriteFullEndElement(); 
    } 
} 

Essa può quindi essere invocato in questo modo (ancora una volta molto simile):

var x_settings = new XmlWriterSettings(); 
x_settings.NewLineChars = Environment.NewLine; 
x_settings.NewLineOnAttributes = true; 
x_settings.NewLineHandling = NewLineHandling.None; 
x_settings.CloseOutput = true; 
x_settings.Indent = true; 
x_settings.NewLineOnAttributes = true; 

using (XmlWriter writer = XmlWriter.Create(outputFilename, x_settings)) 
{ 
    using (XmlTextWriterFull2 xmlTextWriterFull = new XmlTextWriterFull2(writer)) 
    { 
     var x_serial = new XmlSerializer(typeof(YOUR_OBJECT_TYPE)); 
     x_serial.Serialize(xmlTextWriterFull, YOUR_OBJECT_INSTANCE); 
    } 
} 

Nel mio caso, un elemento che era stato precedentemente reso come

<Foo> 
</Foo> 

diventato

<Foo></Foo> 

Come accennato nella sua interrogazione, questo è in realtà piuttosto un problema difficile a causa di tutto ciò che viene sigillato/interno, ecc, rendendo le sostituzioni piuttosto difficile. Penso che il mio più grande problema è stato quello di provare ad ottenere un XmlWriter per accettare XmlWriterSettings: oltre questo approccio, non ho trovato alcun modo di ottenere il XmlTextWriterFull originale per rispettare il dato XmlWriterSettings.

MSDN afferma che questo metodo:

XmlWriter.Create(XmlWriter, XmlWriterSettings) 

Può essere utilizzato per applicare il XmlWriterSettings al XmlWriter. Non riuscivo a far funzionare tutto ciò che volevo (il rientro non ha funzionato, per esempio), e dopo aver decompilato il codice, non sembra che tutte le impostazioni siano usate con questo particolare metodo, quindi perché il mio codice di invocazione è appena passato il outputFile (un flusso di qualche tipo funzionerebbe altrettanto bene).

0

Un'altra opzione.

public class XmlCustomTextWriter : XmlTextWriter 
{ 
    private TextWriter _tw = null; 

    public XmlCustomTextWriter(TextWriter sink) 
     : base(sink) 
    { 
     _tw = sink; 
     Formatting = Formatting.Indented; 
     Indentation = 0; 
    } 

    public void OutputElement(string name, string value) 
    { 
     WriteStartElement(name); 
     string nl = _tw.NewLine; 
     _tw.NewLine = ""; 
     WriteString(value); 
     WriteFullEndElement(); 
     _tw.NewLine = nl; 
    } 
} 
2

La soluzione soluzione hai dato nella tua domanda aggiunge interruzioni di riga in più (quando è abilitato indentazione) perché stiamo dicendo lo scrittore per il trattamento di questo elemento come se avesse figli.

Ecco come ho modificato la soluzione alternativa per manipolare il rientro dinamicamente in modo da evitare quelle interruzioni di riga aggiuntive.

public class XmlTextWriterFull : XmlTextWriter 
{ 
    public XmlTextWriterFull(TextWriter sink) 
     : base(sink) 
    { 
     Formatting = Formatting.Indented; 
    } 

    private bool inElement = false; 

    public override void WriteStartElement(string prefix, string localName, string ns) 
    { 
     base.WriteStartElement(prefix, localName, ns); 

     // Remember that we're in the process of defining an element. 
     // As soon as a child element is closed, this flag won't be true anymore and we'll know to avoid messing with the indenting. 
     this.inElement = true; 
    } 

    public override void WriteEndElement() 
    { 
     if (!this.inElement) 
     { 
      // The element being closed has child elements, so we should just let the writer use it's default behavior. 
      base.WriteEndElement(); 
     } 
     else 
     { 
      // It looks like the element doesn't have children, and we want to avoid emitting a self-closing tag. 
      // First, let's temporarily disable any indenting, then force the full closing element tag. 
      var prevFormat = this.Formatting; 
      this.Formatting = Formatting.None; 
      base.WriteFullEndElement(); 
      this.Formatting = prevFormat; 
      this.inElement = false; 
     } 
    } 
} 
0

Lasciando questo qui nel caso qualcuno ne abbia bisogno; poiché nessuna delle risposte di cui sopra ha risolto per me, o sembrava eccessivo.

FileStream fs = new FileStream("file.xml", FileMode.Create); 
    XmlWriterSettings settings = new XmlWriterSettings(); 
    settings.Indent = true; 
    XmlWriter w = XmlWriter.Create(fs, settings); 
    w.WriteStartDocument(); 
    w.WriteStartElement("tag1"); 

     w.WriteStartElement("tag2"); 
     w.WriteAttributeString("attr1", "val1"); 
     w.WriteAttributeString("attr2", "val2"); 
     w.WriteFullEndElement(); 

    w.WriteEndElement(); 
    w.WriteEndDocument(); 
    w.Flush(); 
    fs.Close(); 

Il trucco era quello di impostare la XmlWriterSettings.Indent = true e aggiungerlo alla XmlWriter.

Edit:

In alternativa è anche possibile utilizzare

w.Formatting = Formatting.Indented; 

invece di aggiungere un XmlWriterSettings.

0

A seguito di codice forza frammento di stampa del tag di chiusura sulla stessa linea (mi dispiace per la versione VB, dovrebbe essere facile per riscrivere lo stesso utilizzando C#):

Imports System.Xml 
Imports System.IO 
Public Class CustomXmlTextWriter 
    Inherits XmlTextWriter 

    Public Sub New(ByRef baseWriter As TextWriter) 
     MyBase.New(baseWriter) 
     Formatting = Xml.Formatting.Indented 
    End Sub 

    Public Overrides Sub WriteEndElement() 
     If Not (Me.WriteState = Xml.WriteState.Element) Then 
      MyBase.WriteEndElement() 
     Else 
      Formatting = Xml.Formatting.None 
      MyBase.WriteFullEndElement() 
      Formatting = Xml.Formatting.Indented 
     End If 
    End Sub 

End Class 
Problemi correlati