2012-11-15 12 views
8

Pracuję ze starszą aplikacją, która nie importuje skróconych pustych elementów xml. Na przykład:Jak używać XmlWriterSettings() przy użyciu zastępowania void WriteEndElement()?

BAD pusty:

<foo /> 

DOBRY pusty:

<foo></foo> 

Wiem, że rozwiązanie to osiągnąć, które przedstawię teraz:

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

i kod klienta:

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

Jednakże, jeśli zaobserwowałeś ostrożnie, XmlWriterSettings nigdy nie są używane w kodzie klienta. Dlatego wyjściowe dane XML są strasznie sformatowane. Moje pytania są następujące: w jaki sposób mogę dostosować powyższy kod do akceptacji XmlWriterSettings?

Stosowanie metod tworzenia fabryki i klas szczelnych/wewnętrznych/abstrakcyjnych utrudnia implementację zastąpienia.

Przyjmuję alternatywne rozwiązanie, nie jestem żonaty z powyższym rozwiązaniem.

  • obejście problemu

Krok 1: tworzenie następujące klasy w roztworze:

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

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

Krok 2: Dodaj następujący kod klienta. Upewnij się, aby zastąpić YOUR_OBJECT_TYPE i YOUR_OBJECT_INSTANCE z klasą i przykład swojej pracy z:

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

Rozwiązaniem powyżej będzie produkować następujący element pusty xml formatowania:

<foo> 
</foo> 

Problem z tym obejściem problemu jest, że dodaje wiersz (zauważ, że elementy są w oddzielnych wierszach). To może być dla ciebie akceptowalne, ale powoduje problemy z moją starszą aplikacją.

+0

Nie wiem, czy rozwiązuje to ten szczególny przypadek , ale możesz użyć przeciążenia XmlWriter.Create, które pobiera istniejący program piszący i ustawienia. Możesz przekazać własną implementację do tej metody. Według http://msdn.microsoft.com/en-us/library/a09119h4.aspx umożliwia to "dodanie dodatkowych funkcji do bazowego obiektu XmlWriter", ale nie wiem, czy to obejmuje formatowanie. – fsimonazzi

+0

Nie byłem w stanie odpowiedzieć na moje własne pytanie. Ale znalazłem dopuszczalne rozwiązanie. Opublikuję to tutaj, aby, miejmy nadzieję, pomóc komuś innemu; jednak nadal chcę rozwiązania mojego pytania. Moje obejście nie używa XmlWriterSettings, czego właśnie chcę. – sapbucket

+0

Aktualizacja: moja starsza aplikacja nie akceptuje powyższego obejścia, ponieważ pełny znacznik ma wstawiony element posuwu liniowego; dlatego tagi pojawiają się w osobnych wierszach. Publikuję nagrodę, aby sprawdzić, czy mogę zwrócić większą uwagę na to pytanie. – sapbucket

Odpowiedz

4

Co powiesz na to.

Złap za wspaniałą klasę XmlWrappingWriter od http://www.tkachenko.com/blog/archives/000585.html (pominąłem kod ze względu na zwięzłość).

Dzięki temu możemy stworzyć podklasę następująco (bardzo podobny do swojego pierwotnego):

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

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

To może być wywołana w ten sposób (znów bardzo podobnie):

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

W moim przypadku, to element, który wcześniej został nadany jako

<Foo> 
</Foo> 

stał

<Foo></Foo> 

Jak wspomniał w swoim pytaniu, to jest rzeczywiście dość trudne ze względu na problem, wszystko jest zamknięte/wewnętrzne itd., Dzięki czemu zastępuje raczej trudne. Myślę, że moim największym problemem było uzyskanie XmlWriter do zaakceptowania XmlWriterSettings: poza tym podejściem, nie mogłem znaleźć żadnego sposobu na uzyskanie oryginalnego XmlTextWriterFull, aby respektować dane XmlWriterSettings.

MSDN stwierdza, że ​​ta metoda:

XmlWriter.Create(XmlWriter, XmlWriterSettings) 

Może być używany do zastosowania XmlWriterSettings do XmlWriter. Nie mogłem tego zrobić tak, jakbym chciał (na przykład wcięcie nigdy nie działało), a po dekompilacji kodu nie wydaje się, że wszystkie ustawienia są używane z tą konkretną metodą, dlatego mój kod wywołania po prostu przechodzi outputFile (jakiś strumień działałby równie dobrze).

0

Inna opcja.

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

Rozwiązanie obejście dałeś w swoim pytaniu dodaje dodatkowe podziały wiersza (gdy wcięć jest włączona), ponieważ mówimy pisarza traktować ten element tak, jakby miał dzieci.

Oto jak zmodyfikowałem twoje obejście, aby dynamicznie manipulować wcięciami, aby uniknąć dodatkowych przerw w linii.

0

Pozostawienie tego tutaj, na wypadek, gdyby ktoś tego potrzebował; ponieważ żadna z powyższych odpowiedzi nie rozwiązała tego dla mnie lub nie wydawała mi się przesadna.

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

Sztuką było ustawić XmlWriterSettings.Indent = true i dodać go do XmlWriter.

Edit:

Alternatywnie można również użyć

w.Formatting = Formatting.Indented; 

zamiast dodawania XmlWriterSettings.

0

Po druku kod siły fragment zamykający znacznik na tej samej linii (przepraszam za wersją VB, to powinno być łatwe do przerobienia samo przy użyciu 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 
Powiązane problemy