Sformatuj ciąg XML, aby wydrukować przyjazny ciąg XML

178

Mam taki ciąg XML:

<?xml version='1.0'?><response><error code='1'> Success</error></response>

Nie ma linii między jednym elementem a drugim, dlatego jest bardzo trudny do odczytania. Chcę funkcji, która formatuje powyższy ciąg:

<?xml version='1.0'?>
<response>
<error code='1'> Success</error>
</response> 

Bez uciekania się do ręcznego pisania funkcji formatu, czy jest jakaś biblioteka .Net lub fragment kodu, którego mogę użyć od ręki?

Graviton
źródło
1
props do CMS, pytanie jest duplikatem stackoverflow.com/questions/203528
Spence,
2
To nie jest duplikat. To określa, XmlDocumentktóra zdyskwalifikuje najwyższą głosowaną odpowiedź na to pytanie.
sirdank

Odpowiedzi:

185

Użyj XmlTextWriter...

public static string PrintXML(string xml)
{
    string result = "";

    MemoryStream mStream = new MemoryStream();
    XmlTextWriter writer = new XmlTextWriter(mStream, Encoding.Unicode);
    XmlDocument document = new XmlDocument();

    try
    {
        // Load the XmlDocument with the XML.
        document.LoadXml(xml);

        writer.Formatting = Formatting.Indented;

        // Write the XML into a formatting XmlTextWriter
        document.WriteContentTo(writer);
        writer.Flush();
        mStream.Flush();

        // Have to rewind the MemoryStream in order to read
        // its contents.
        mStream.Position = 0;

        // Read MemoryStream contents into a StreamReader.
        StreamReader sReader = new StreamReader(mStream);

        // Extract the text from the StreamReader.
        string formattedXml = sReader.ReadToEnd();

        result = formattedXml;
    }
    catch (XmlException)
    {
        // Handle the exception
    }

    mStream.Close();
    writer.Close();

    return result;
}
SM Kamran
źródło
7
Działa to, jeśli masz do czynienia z kodem, który znajduje się w starej wersji platformy .NET przed LINQ, ale drugi przykład jest znacznie bardziej przejrzysty.
Mike,
8
Aby wyjaśnić komentarz Mike'a: LINQ został wprowadzony w .NET 3.5. Więc jeśli używasz starszej wersji .NET (.NET 1, 1.1, 2 lub 3.0), będziesz musiał użyć tej odpowiedzi. Ale jeśli używasz .NET 3.5 lub nowszego, odpowiedź Charlesa Prakasha Dasariego jest dużo prostsza.
Simon Tewsi
1
@SM Kamran Używam twojego kodu, ale pojawia się błąd, który wygląda jak {"Nie można uzyskać dostępu do zamkniętego strumienia."} Na writer.Close (); pls dają rozwiązanie.
Jatin Gadhiya
@JatinGadhiya Miałem ten sam problem i rozwiązałem go używając {using block} w definiowaniu strumieni. w ten sposób nie ma potrzeby ręcznego zamykania strumienia, a strumienie zostaną automatycznie zamknięte po osiągnięciu końca używanego bloku.
Vahid Farahmandian
312

Będziesz musiał jakoś przeanalizować zawartość ... Uważam, że użycie LINQ jest najłatwiejszym sposobem na zrobienie tego. Znowu wszystko zależy od twojego dokładnego scenariusza. Oto działający przykład użycia LINQ do sformatowania wejściowego ciągu XML.

string FormatXml(string xml)
{
     try
     {
         XDocument doc = XDocument.Parse(xml);
         return doc.ToString();
     }
     catch (Exception)
     {
         // Handle and throw if fatal exception here; don't just ignore them
         return xml;
     }
 }

[używanie oświadczeń jest pomijane ze względu na zwięzłość]

Charles Prakash Dasari
źródło
Czy wpłynie to ściśle na podziały wierszy i wcięcia? Nie chcę żadnych innych zmian, takich jak „0” zmieniane na „0,0” itp. Gdy wszystkie białe znaki zostaną usunięte, chcę, aby pozbawiony łańcuch wynikowy był dokładnie taki sam, jak usunięty ciąg wejściowy.
Radim Cernej
3
@radim Yes. Żadne zmiany w rzeczywistych danych nie zostaną wprowadzone. Tylko tagi zostaną sformatowane i z wcięciami.
Charles Prakash Dasari
2
Zauważyłem, że działa dobrze z UTF8, ale nie z zawartością pliku XML Unicode.
Nayan
1
@SteveWellens, możesz uzyskać dostęp do deklaracji za pośrednictwem doc.Declaration.ToString() + doc.ToString()lub używając doc.Savezamiast doc.ToString. Zobacz ten link, aby uzyskać więcej informacji.
David French
1
Zasugeruj uwzględnienie przestrzeni nazw, ponieważ zapobiega to konieczności wyszukiwania przestrzeni nazw dla klasy, której wcześniej nie używali zbyt często. using System.Xml.Linq; Działa dobrze Dzięki!
Scott Moniz
61

Ten od kristopherjohnson jest o wiele lepszy:

  1. Nie wymaga też nagłówka dokumentu XML.
  2. Ma wyraźniejsze wyjątki
  3. Dodaje dodatkowe opcje zachowania: OmitXmlDeclaration = true, NewLineOnAttributes = true
  4. Mniej linii kodu

    static string PrettyXml(string xml)
    {
        var stringBuilder = new StringBuilder();
    
        var element = XElement.Parse(xml);
    
        var settings = new XmlWriterSettings();
        settings.OmitXmlDeclaration = true;
        settings.Indent = true;
        settings.NewLineOnAttributes = true;
    
        using (var xmlWriter = XmlWriter.Create(stringBuilder, settings))
        {
            element.Save(xmlWriter);
        }
    
        return stringBuilder.ToString();
    }
    
Todd
źródło
Todd, czy mógłbyś wyjaśnić, co masz na myśli mówiąc „nie wymaga nagłówka dokumentu XML”? Wypróbowałem rozwiązanie Charlesa Prakasha Dasariego i właśnie przekazałem fragment XML bez deklaracji xml (tj. Bez <?xml version="1.0" encoding="UTF-8" ?>linii na górze) i działało dobrze.
Simon Tewsi,
3
W porównaniu z zaakceptowaną odpowiedzią. W porównaniu z Charlesem ten miałby lepszą konfigurowalność. Jednak prawdopodobnie sam użyłbym metody Charlies w przyszłości, taka konfigurowalność byłaby rzadkim wymogiem.
Todd
1
Ten jest znacznie lepszy i krótszy
Alex Jolig
8

Proste rozwiązanie, które działa dla mnie:

        XmlDocument xmlDoc = new XmlDocument();
        StringWriter sw = new StringWriter();
        xmlDoc.LoadXml(rawStringXML);
        xmlDoc.Save(sw);
        String formattedXml = sw.ToString();
ZeeProgrammer
źródło
spowoduje to utworzenie pliku xml z nagłówkiem <? xml version = "1.0" encoding = "utf-16"?>. Nie zostało to przeanalizowane przez XmlSerializer, z powodu błędu „Brak znacznika kolejności bajtów Unicode”. Poprawka polegała na usunięciu encoding = "utf-16", patrz: stackoverflow.com/questions/29915467/… .
Declan Taylor
6

Sprawdź poniższy link: Jak ładnie wydrukować XML (Niestety, łącze zwraca teraz 404 :()

Metoda w linku przyjmuje ciąg XML jako argument i zwraca poprawnie sformułowany (z wcięciem) ciąg XML.

Właśnie skopiowałem przykładowy kod z linku, aby ta odpowiedź była bardziej wszechstronna i wygodna.

public static String PrettyPrint(String XML)
{
    String Result = "";

    MemoryStream MS = new MemoryStream();
    XmlTextWriter W = new XmlTextWriter(MS, Encoding.Unicode);
    XmlDocument D   = new XmlDocument();

    try
    {
        // Load the XmlDocument with the XML.
        D.LoadXml(XML);

        W.Formatting = Formatting.Indented;

        // Write the XML into a formatting XmlTextWriter
        D.WriteContentTo(W);
        W.Flush();
        MS.Flush();

        // Have to rewind the MemoryStream in order to read
        // its contents.
        MS.Position = 0;

        // Read MemoryStream contents into a StreamReader.
        StreamReader SR = new StreamReader(MS);

        // Extract the text from the StreamReader.
        String FormattedXML = SR.ReadToEnd();

        Result = FormattedXML;
    }
    catch (XmlException)
    {
    }

    MS.Close();
    W.Close();

    return Result;
}
Chansik Im
źródło
2
U mnie działa świetnie, właśnie zrobiłem z tego metodę rozszerzającą string. Również ta strona internetowa nie działa, więc dobrze, że
złapałeś
1
Zduplikowana odpowiedź. @SM Kamran również publikuje tę samą odpowiedź.
Vahid Farahmandian
@VahidFarahmandian Yes. Nie mogłem wiele z tym zrobić, ponieważ opublikowałem 1 minutę wcześniej niż on :) Przy okazji, próbowałem dodać skąd pochodzi odpowiedź, aby przypisać autorstwo plakatu na blogu. Niestety, link jest teraz uszkodzony :(.
Chansik Im
Najbardziej podoba mi się ta odpowiedź w porównaniu z tą od Charlesa (FormatXml) i Todda (PrettyXml), ponieważ ta odpowiedź nie usuwa <?xml...?>linii. Ta odpowiedź zawiera to, co pierwotnie miałem na myśli. Jedynym minusem byłoby to, że wolałbym tabulatory niż spacje używane natywnie. Ustawiłem Indentation = 1i IndentChar = '\t'otrzymałem dokładnie to, czego chciałem.
Sarah Weinberger
@ CHICoder007 Dziękujemy za komentarz dotyczący metody rozszerzenia. Nauczyłeś mnie czegoś nowego. Dodanie a (this String XML)działa świetnie.
Sarah Weinberger,
4

Próbowałem:

internal static void IndentedNewWSDLString(string filePath)
{
    var xml = File.ReadAllText(filePath);
    XDocument doc = XDocument.Parse(xml);
    File.WriteAllText(filePath, doc.ToString());
}

działa dobrze, zgodnie z oczekiwaniami.

Akhilesh singh
źródło
ale to usuwa znacznik <? xml?> na górze
Juran
2

.NET 2.0 ignoruje rozpoznawanie nazw, z odpowiednim usuwaniem zasobów, wcięciami, zachowywaniem białych znaków i niestandardowym kodowaniem :

public static string Beautify(System.Xml.XmlDocument doc)
{
    string strRetValue = null;
    System.Text.Encoding enc = System.Text.Encoding.UTF8;
    // enc = new System.Text.UTF8Encoding(false);

    System.Xml.XmlWriterSettings xmlWriterSettings = new System.Xml.XmlWriterSettings();
    xmlWriterSettings.Encoding = enc;
    xmlWriterSettings.Indent = true;
    xmlWriterSettings.IndentChars = "    ";
    xmlWriterSettings.NewLineChars = "\r\n";
    xmlWriterSettings.NewLineHandling = System.Xml.NewLineHandling.Replace;
    //xmlWriterSettings.OmitXmlDeclaration = true;
    xmlWriterSettings.ConformanceLevel = System.Xml.ConformanceLevel.Document;


    using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
    {
        using (System.Xml.XmlWriter writer = System.Xml.XmlWriter.Create(ms, xmlWriterSettings))
        {
            doc.Save(writer);
            writer.Flush();
            ms.Flush();

            writer.Close();
        } // End Using writer

        ms.Position = 0;
        using (System.IO.StreamReader sr = new System.IO.StreamReader(ms, enc))
        {
            // Extract the text from the StreamReader.
            strRetValue = sr.ReadToEnd();

            sr.Close();
        } // End Using sr

        ms.Close();
    } // End Using ms


    /*
    System.Text.StringBuilder sb = new System.Text.StringBuilder(); // Always yields UTF-16, no matter the set encoding
    using (System.Xml.XmlWriter writer = System.Xml.XmlWriter.Create(sb, settings))
    {
        doc.Save(writer);
        writer.Close();
    } // End Using writer
    strRetValue = sb.ToString();
    sb.Length = 0;
    sb = null;
    */

    xmlWriterSettings = null;
    return strRetValue;
} // End Function Beautify

Stosowanie:

System.Xml.XmlDocument xmlDoc = new System.Xml.XmlDocument();
xmlDoc.XmlResolver = null;
xmlDoc.PreserveWhitespace = true;
xmlDoc.Load("C:\Test.svg");
string SVG = Beautify(xmlDoc);
Stefan Steiger
źródło
0

jeśli załadujesz XMLDoc, jestem prawie pewien, że funkcja .ToString () ma w tym celu przeciążenie.

Ale czy to służy do debugowania? Powodem, dla którego jest wysyłany w ten sposób, jest to, że zajmuje mniej miejsca (tj. Usuwanie niepotrzebnych białych znaków z XML).

Spence
źródło
0

Konfigurowalne wyjście Pretty XML z deklaracją XML UTF-8

Poniższa definicja klasy zawiera prostą metodę konwersji wejściowego ciągu XML na sformatowany wyjściowy kod XML z deklaracją xml jako UTF-8. Obsługuje wszystkie opcje konfiguracji, które oferuje klasa XmlWriterSettings .

using System;
using System.Text;
using System.Xml;
using System.IO;

namespace CJBS.Demo
{
    /// <summary>
    /// Supports formatting for XML in a format that is easily human-readable.
    /// </summary>
    public static class PrettyXmlFormatter
    {

        /// <summary>
        /// Generates formatted UTF-8 XML for the content in the <paramref name="doc"/>
        /// </summary>
        /// <param name="doc">XmlDocument for which content will be returned as a formatted string</param>
        /// <returns>Formatted (indented) XML string</returns>
        public static string GetPrettyXml(XmlDocument doc)
        {
            // Configure how XML is to be formatted
            XmlWriterSettings settings = new XmlWriterSettings 
            {
                Indent = true
                , IndentChars = "  "
                , NewLineChars = System.Environment.NewLine
                , NewLineHandling = NewLineHandling.Replace
                //,NewLineOnAttributes = true
                //,OmitXmlDeclaration = false
            };

            // Use wrapper class that supports UTF-8 encoding
            StringWriterWithEncoding sw = new StringWriterWithEncoding(Encoding.UTF8);

            // Output formatted XML to StringWriter
            using (XmlWriter writer = XmlWriter.Create(sw, settings))
            {
                doc.Save(writer);
            }

            // Get formatted text from writer
            return sw.ToString();
        }



        /// <summary>
        /// Wrapper class around <see cref="StringWriter"/> that supports encoding.
        /// Attribution: http://stackoverflow.com/a/427737/3063884
        /// </summary>
        private sealed class StringWriterWithEncoding : StringWriter
        {
            private readonly Encoding encoding;

            /// <summary>
            /// Creates a new <see cref="PrettyXmlFormatter"/> with the specified encoding
            /// </summary>
            /// <param name="encoding"></param>
            public StringWriterWithEncoding(Encoding encoding)
            {
                this.encoding = encoding;
            }

            /// <summary>
            /// Encoding to use when dealing with text
            /// </summary>
            public override Encoding Encoding
            {
                get { return encoding; }
            }
        }
    }
}

Możliwości dalszej poprawy: -

  • GetPrettyXml(XmlDocument doc, XmlWriterSettings settings)Można utworzyć dodatkową metodę, która pozwoli wywołującemu dostosować dane wyjściowe.
  • GetPrettyXml(String rawXml)Można dodać dodatkową metodę, która obsługuje analizowanie nieprzetworzonego tekstu, zamiast używać klienta XmlDocument. W moim przypadku musiałem manipulować XML za pomocą XmlDocument, dlatego nie dodałem tego.

Stosowanie:

String myFormattedXml = null;
XmlDocument doc = new XmlDocument();
try
{
    doc.LoadXml(myRawXmlString);
    myFormattedXml = PrettyXmlFormatter.GetPrettyXml(doc);
}
catch(XmlException ex)
{
    // Failed to parse XML -- use original XML as formatted XML
    myFormattedXml = myRawXmlString;
}
CJBS
źródło