Jak radzić sobie z XML w C #

85

Jaki jest najlepszy sposób radzenia sobie z dokumentami XML, XSD itp. W C # 2.0?

Których klas użyć itp. Jakie są najlepsze praktyki dotyczące analizowania i tworzenia dokumentów XML itp.

EDYCJA: mile widziane są również sugestie .Net 3.5.

Malik Daud Ahmad Khokhar
źródło
Dla tych, którzy również próbują znaleźć bardziej wykonalne rozwiązanie, zignoruj ​​to. Jest to stara biblioteka .NET. Zamiast tego użyj XDocument, a zaoszczędzisz sobie żłobienia oczu z frustracji
AER

Odpowiedzi:

177

Podstawowe sposoby czytania i pisania w języku C # 2.0 są realizowane za pośrednictwem klasy XmlDocument . Większość ustawień można załadować bezpośrednio do XmlDocument za pośrednictwem XmlReader, które akceptuje.

Ładowanie XML bezpośrednio

XmlDocument document = new XmlDocument();
document.LoadXml("<People><Person Name='Nick' /><Person Name='Joe' /></People>");

Ładowanie XML z pliku

XmlDocument document = new XmlDocument();
document.Load(@"C:\Path\To\xmldoc.xml");
// Or using an XmlReader/XmlTextReader
XmlReader reader = XmlReader.Create(@"C:\Path\To\xmldoc.xml");
document.Load(reader);

Uważam, że najłatwiejszym / najszybszym sposobem odczytania dokumentu XML jest użycie XPath.

Czytanie dokumentu XML za pomocą XPath (przy użyciu XmlDocument, który pozwala nam edytować)

XmlDocument document = new XmlDocument();
document.LoadXml("<People><Person Name='Nick' /><Person Name='Joe' /></People>");

// Select a single node
XmlNode node = document.SelectSingleNode("/People/Person[@Name = 'Nick']");

// Select a list of nodes
XmlNodeList nodes = document.SelectNodes("/People/Person");

Jeśli potrzebujesz pracować z dokumentami XSD, aby sprawdzić poprawność dokumentu XML, możesz tego użyć.

Sprawdzanie poprawności dokumentów XML pod kątem schematów XSD

XmlReaderSettings settings = new XmlReaderSettings();
settings.ValidateType = ValidationType.Schema;
settings.Schemas.Add("", pathToXsd); // targetNamespace, pathToXsd

XmlReader reader = XmlReader.Create(pathToXml, settings);
XmlDocument document = new XmlDocument();

try {
    document.Load(reader);
} catch (XmlSchemaValidationException ex) { Trace.WriteLine(ex.Message); }

Sprawdzanie poprawności XML względem XSD w każdym węźle (UPDATE 1)

XmlReaderSettings settings = new XmlReaderSettings();
settings.ValidateType = ValidationType.Schema;
settings.Schemas.Add("", pathToXsd); // targetNamespace, pathToXsd
settings.ValidationEventHandler += new ValidationEventHandler(settings_ValidationEventHandler);

XmlReader reader = XmlReader.Create(pathToXml, settings);
while (reader.Read()) { }

private void settings_ValidationEventHandler(object sender, ValidationEventArgs args)
{
    // e.Message, e.Severity (warning, error), e.Error
    // or you can access the reader if you have access to it
    // reader.LineNumber, reader.LinePosition.. etc
}

Pisanie dokumentu XML (ręcznie)

XmlWriter writer = XmlWriter.Create(pathToOutput);
writer.WriteStartDocument();
writer.WriteStartElement("People");

writer.WriteStartElement("Person");
writer.WriteAttributeString("Name", "Nick");
writer.WriteEndElement();

writer.WriteStartElement("Person");
writer.WriteStartAttribute("Name");
writer.WriteValue("Nick");
writer.WriteEndAttribute();
writer.WriteEndElement();

writer.WriteEndElement();
writer.WriteEndDocument();

writer.Flush();

(AKTUALIZACJA 1)

W .NET 3.5 używasz XDocument do wykonywania podobnych zadań. Różnica polega jednak na tym, że masz tę zaletę, że wykonujesz zapytania Linq, aby wybrać dokładne dane, których potrzebujesz. Dzięki dodaniu inicjatorów obiektów można utworzyć zapytanie, które zwraca nawet obiekty z własnej definicji bezpośrednio w samym zapytaniu.

    XDocument doc = XDocument.Load(pathToXml);
    List<Person> people = (from xnode in doc.Element("People").Elements("Person")
                       select new Person
                       {
                           Name = xnode.Attribute("Name").Value
                       }).ToList();

(AKTUALIZACJA 2)

Przyjemnym sposobem w .NET 3.5 jest użycie XDocument do tworzenia XML. To sprawia, że ​​kod pojawia się w podobny sposób do żądanego wyniku.

XDocument doc =
        new XDocument(
              new XDeclaration("1.0", Encoding.UTF8.HeaderName, String.Empty),
              new XComment("Xml Document"),
              new XElement("catalog",
                    new XElement("book", new XAttribute("id", "bk001"),
                          new XElement("title", "Book Title")
                    )
              )
        );

tworzy

<!--Xml Document-->
<catalog>
  <book id="bk001">
    <title>Book Title</title>
  </book>
</catalog>

Wszystko inne zawodzi, możesz zapoznać się z tym artykułem MSDN, który zawiera wiele przykładów, które tutaj omówiłem, i nie tylko. http://msdn.microsoft.com/en-us/library/aa468556.aspx

nyxtom
źródło
3
Możesz zwrócić uwagę, że używasz XDocument w ostatnim przykładzie, ponieważ XDocument różni się od XmlDocument
Aaron Powell
2
Korekta; nie ma C # 3.5; masz na myśli .NET 3.5 i C # 3.0
Marc Gravell
och, a „w locie” [inicjatory obiektów] działałyby w dużej mierze tak samo z C # 3.0 i XmlDocument - ale nadal dobra odpowiedź (+1)
Marc Gravell
Warto wspomnieć, że jeśli ładujesz dokument do zapytania za pomocą XPath (a nie edytujesz), to użycie XPathDocument będzie o wiele bardziej wydajne
Oliver Hallam
Czy ta walidacja schematu jest wykonywana węzeł po węźle? Jeśli nie, czy istnieje sposób, aby to zrobić węzeł po węźle?
Malik Daud Ahmad Khokhar
30

To zależy od rozmiaru; w przypadku małych i średnich plików XML DOM, taki jak XmlDocument (dowolna wersja C # / .NET) lub XDocument (.NET 3.5 / C # 3.0) jest oczywistym zwycięzcą. W przypadku korzystania z xsd można załadować plik XML przy użyciu XmlReader , a XmlReader akceptuje (do utworzenia ) XmlReaderSettings . Obiekty XmlReaderSettings mają właściwość Schemas, której można użyć do wykonania walidacji xsd (lub dtd).

W przypadku pisania xml obowiązują te same rzeczy, zauważając, że nieco łatwiej jest rozłożyć zawartość za pomocą LINQ-to-XML (XDocument) niż starszy XmlDocument.

Jednak w przypadku ogromnego pliku XML DOM może zepsuć zbyt dużo pamięci, w takim przypadku może być konieczne bezpośrednie użycie XmlReader / XmlWriter.

Na koniec do manipulowania XML możesz chcieć użyć XslCompiledTransform (warstwy xslt).

Alternatywą dla pracy z XML jest praca z modelem obiektowym; możesz użyć xsd.exe do tworzenia klas, które reprezentują model zgodny z xsd i po prostu załaduj xml jako obiekty , manipuluj nim za pomocą OO, a następnie ponownie serializuj te obiekty; robisz to za pomocą XmlSerializer .

Marc Gravell
źródło
Aby manipulować (dodawać / wspierać elementy) dużym dokumentem XML (40 tys. Wierszy). Jaki jest najlepszy sposób? Kiedyś używałem LINQ-to-XML.
Neyoh
12

odpowiedź nyxtom jest bardzo dobra. Dodałbym do tego kilka rzeczy:

Jeśli potrzebujesz dostępu tylko do odczytu do dokumentu XML, XPathDocumentjest to znacznie lżejszy obiekt niż XmlDocument.

Wadą używania XPathDocumentjest to, że nie możesz używać znanych SelectNodesi SelectSingleNodemetod XmlNode. Zamiast tego musisz użyć narzędzi, które IXPathNavigablezapewnia: użyj, CreateNavigatoraby utworzyć XPathNavigator, i użyj, XPathNavigatoraby utworzyć XPathNodeIterators, aby iterować po listach węzłów znalezionych za pośrednictwem XPath. Zwykle wymaga to kilku wierszy kodu więcej niż XmlDocumentmetody.

Ale: klasy XmlDocumenti są XmlNodeimplementowane IXPathNavigable, więc każdy kod, który napiszesz, aby użyć tych metod w pliku, XPathDocumentbędzie również działał na pliku XmlDocument. Jeśli przyzwyczaisz się do pisania przeciwko IXPathNavigable, twoje metody mogą działać przeciwko obu obiektom. (Dlatego używanie sygnatur metod XmlNodei XmlDocumentw jest oznaczane przez FxCop).

Żałośnie XDocumenti XElement( XNodei XObject) nie wdrażają IXPathNavigable.

Inną rzeczą, której nie ma w odpowiedzi nyxtom, jest XmlReader. Zwykle używa się go, XmlReaderaby uniknąć narzutu związanego z analizowaniem strumienia XML do modelu obiektowego przed rozpoczęciem jego przetwarzania. Zamiast tego używaszXmlReader do przetwarzania strumienia wejściowego jednego węzła XML na raz. To jest zasadniczo odpowiedź .NET na SAX. Pozwala pisać bardzo szybki kod do przetwarzania bardzo dużych dokumentów XML.

XmlReader zapewnia również najprostszy sposób przetwarzania fragmentów dokumentów XML, np. strumień elementów XML bez elementu kodującego, który zwraca opcja FOR XML RAW programu SQL Server.

Kod, którego używasz, XmlReaderjest zazwyczaj bardzo ściśle powiązany z formatem czytanego XML. Używanie XPath pozwala na dużo, dużo luźniejsze powiązanie twojego kodu z XML, dlatego jest to zazwyczaj właściwa odpowiedź. Ale kiedy potrzebujesz użyć XmlReader, naprawdę tego potrzebujesz.

Robert Rossney
źródło
3
Zauważ, że istnieje metoda rozszerzająca XPathNavigator CreateNavigator(this XNode node)do tworzenia XPathNavigatoran XNode(która zawiera klasę pochodną XDocument).
Dave,
5

Przede wszystkim poznaj nowe klasy XDocument i XElement , ponieważ są one ulepszeniem w stosunku do poprzedniej rodziny XmlDocument.

  1. Współpracują z LINQ
  2. Są szybsze i lżejsze

Jednak może być konieczne używanie starych klas do pracy ze starszym kodem - w szczególności z wcześniej wygenerowanymi serwerami proxy. W takim przypadku będziesz musiał zapoznać się z niektórymi wzorcami współdziałania między tymi klasami obsługi XML.

Myślę, że twoje pytanie jest dość szerokie i wymagałoby zbyt wiele w jednej odpowiedzi, aby podać szczegóły, ale jest to pierwsza ogólna odpowiedź, o której pomyślałem, i służy jako początek.

gaj
źródło
Zgadzam się, że (XDocument itp.) Są świetne, ale OP zapytał o C # 2.0.
Marc Gravell
2

Jeśli pracujesz w .NET 3.5 i nie boisz się eksperymentalnego kodu, możesz sprawdzić LINQ to XSD ( http://blogs.msdn.com/xmlteam/archive/2008/02/21/linq-to- xsd-alpha-0-2.aspx ), który wygeneruje klasy .NET z XSD (w tym wbudowane reguły z XSD).

Ma wtedy możliwość zapisu bezpośrednio do pliku i odczytu z pliku, zapewniając zgodność z regułami XSD.

Zdecydowanie sugeruję mieć XSD dla każdego dokumentu XML, z którym pracujesz:

  • Umożliwia egzekwowanie reguł w XML
  • Pozwala innym zobaczyć, jaka jest / będzie struktura XML
  • Może być używany do walidacji XML

Uważam, że Liquid XML Studio to świetne narzędzie do generowania XSD i jest darmowe!

Aaron Powell
źródło
2

Pisanie kodu XML za pomocą klasy XmlDocument

//itemValues is collection of items in Key value pair format
//fileName i name of XML file which to creatd or modified with content
    private void WriteInXMLFile(System.Collections.Generic.Dictionary<string, object> itemValues, string fileName)
    {
        string filePath = "C:\\\\tempXML\\" + fileName + ".xml";
        try
        {

            if (System.IO.File.Exists(filePath))
            {
                XmlDocument doc = new XmlDocument();
                doc.Load(filePath);                   

                XmlNode rootNode = doc.SelectSingleNode("Documents");

                XmlNode pageNode = doc.CreateElement("Document");
                rootNode.AppendChild(pageNode);


                foreach (string key in itemValues.Keys)
                {

                    XmlNode attrNode = doc.CreateElement(key);
                    attrNode.InnerText = Convert.ToString(itemValues[key]);
                    pageNode.AppendChild(attrNode);
                    //doc.DocumentElement.AppendChild(attrNode);

                }
                doc.DocumentElement.AppendChild(pageNode);
                doc.Save(filePath);
            }
            else
            {
                XmlDocument doc = new XmlDocument();
                using(System.IO.FileStream fs = System.IO.File.Create(filePath))
                {
                    //Do nothing
                }

                XmlNode rootNode = doc.CreateElement("Documents");
                doc.AppendChild(rootNode);
                doc.Save(filePath);

                doc.Load(filePath);

                XmlNode pageNode = doc.CreateElement("Document");
                rootNode.AppendChild(pageNode);

                foreach (string key in itemValues.Keys)
                {                          
                    XmlNode attrNode = doc.CreateElement(key);                           
                    attrNode.InnerText = Convert.ToString(itemValues[key]);
                    pageNode.AppendChild(attrNode);
                    //doc.DocumentElement.AppendChild(attrNode);

                }
                doc.DocumentElement.AppendChild(pageNode);

                doc.Save(filePath);

            }
        }
        catch (Exception ex)
        {

        }

    }

OutPut look like below
<Dcouments>
    <Document>
        <DocID>01<DocID>
        <PageName>121<PageName>
        <Author>Mr. ABC<Author>
    <Dcoument>
    <Document>
        <DocID>02<DocID>
        <PageName>122<PageName>
        <Author>Mr. PQR<Author>
    <Dcoument>
</Dcouments>
Anil Rathod
źródło
1

Jeśli utworzysz zbiór danych o typie strukturalnym w projektancie, automatycznie otrzymasz xsd, obiekt o jednoznacznie określonym typie, i możesz załadować i zapisać plik xml za pomocą jednej linii kodu.

Peter C.
źródło
Odniosłem wielki sukces z DataSet. Są również bardzo przyjazne dla baz danych.
Użytkownik 1,
1

Osobiście uważam, że jako programista C # najlepszym sposobem radzenia sobie z XML w C # jest delegowanie tej części kodu do projektu VB .NET. W .NET 3.5, VB .NET ma XML Literals, dzięki czemu obsługa XML jest znacznie bardziej intuicyjna. Zobacz tutaj, na przykład:

Omówienie LINQ to XML w Visual Basic

(Pamiętaj, aby ustawić stronę do wyświetlania kodu VB, a nie kodu C #).

Resztę projektu napisałbym w C #, ale obsłużę XML w przywoływanym projekcie VB.

Ryan Lundy
źródło
Nie warto przełączać się na vb tylko dla literału XML. XML dotyczy tylko literałów. Jeśli plik XML jest przekazywany jako parametr, obsługa literału XML nie daje wielu korzyści. Zamiast tego starsza składnia vb.net zrujnuje przyjemne doświadczenie programowania w C #.
Gqqnbig
0

nyxtom,

Czy „doc” i „xdoc” nie powinny pasować do przykładu 1?

XDocument **doc** = XDocument.Load(pathToXml);
List<Person> people = (from xnode in **xdoc**.Element("People").Elements("Person")
                   select new Person
                   {
                       Name = xnode.Attribute("Name").Value
                   }).ToList();
mokumaxCraig
źródło
Przesłałem do zatwierdzenia zmianę odpowiedzi, do której się odnosisz, jednak to naprawdę powinien być komentarz, a nie odpowiedź.
David Thompson,
Dzięki David. Zgoda, wówczas nie pozwoliłoby mi to komentować. Nie pewny dlaczego.
mokumaxCraig
0

Odpowiedź Cookey jest dobra ... ale tutaj są szczegółowe instrukcje, jak utworzyć obiekt o jednoznacznie określonym typie z XSD (lub XML) i serializować / deserializować w kilku wierszach kodu:

Instrukcje

Steve Horn
źródło
„Strona, której szukasz, nie istnieje”. :(
Ian Grainger,
0

Jeśli kiedykolwiek będziesz musiał konwertować dane między XmlNode<=> XNode<=> XElement
(np. W celu korzystania z LINQ), te rozszerzenia mogą być dla Ciebie pomocne:

public static class MyExtensions
{
    public static XNode GetXNode(this XmlNode node)
    {
        return GetXElement(node);
    }

    public static XElement GetXElement(this XmlNode node)
    {
        XDocument xDoc = new XDocument();
        using (XmlWriter xmlWriter = xDoc.CreateWriter())
            node.WriteTo(xmlWriter);
        return xDoc.Root;
    }

    public static XmlNode GetXmlNode(this XElement element)
    {
        using (XmlReader xmlReader = element.CreateReader())
        {
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.Load(xmlReader);
            return xmlDoc;
        }
    }

    public static XmlNode GetXmlNode(this XNode node)
    {
        return GetXmlNode(node);
    }
}

Stosowanie:

XmlDocument MyXmlDocument = new XmlDocument();
MyXmlDocument.Load("MyXml.xml");
XElement MyXElement = MyXmlDocument.GetXElement(); // Convert XmlNode to XElement
List<XElement> List = MyXElement.Document
   .Descendants()
   .ToList(); // Now you can use LINQ
...
Michael Hutter
źródło