Comment gérer XML en C #

85

Quelle est la meilleure façon de traiter les documents XML, XSD, etc. en C # 2.0?

Quelles classes utiliser, etc. Quelles sont les meilleures pratiques d'analyse et de création de documents XML, etc.

EDIT: Les suggestions .Net 3.5 sont également les bienvenues.

Malik Daud Ahmad Khokhar
la source
Pour ceux qui essaient également de trouver une solution plus viable, ignorez cela. C'est une vieille bibliothèque .NET. Utilisez XDocument à la place et vous éviterez de vous arracher les yeux de frustration
AER

Réponses:

177

Le principal moyen de lecture et d'écriture en C # 2.0 se fait via la classe XmlDocument . Vous pouvez charger la plupart de vos paramètres directement dans le XmlDocument via le XmlReader qu'il accepte.

Chargement direct de XML

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

Chargement de XML à partir d'un fichier

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

Je trouve que le moyen le plus simple / le plus rapide de lire un document XML est d'utiliser XPath.

Lire un document XML à l'aide de XPath (à l'aide de XmlDocument qui nous permet d'éditer)

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

Si vous devez travailler avec des documents XSD pour valider un document XML, vous pouvez l'utiliser.

Validation de documents XML par rapport aux schémas 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); }

Validation de XML par rapport à XSD à chaque nœud (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
}

Ecrire un document XML (manuellement)

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

(MISE À JOUR 1)

Dans .NET 3.5, vous utilisez XDocument pour effectuer des tâches similaires. La différence cependant est que vous avez l'avantage d'exécuter des requêtes Linq pour sélectionner les données exactes dont vous avez besoin. Avec l'ajout d'initialiseurs d'objets, vous pouvez créer une requête qui renvoie même des objets de votre propre définition directement dans la requête elle-même.

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

(MISE À JOUR 2)

Un bon moyen dans .NET 3.5 est d'utiliser XDocument pour créer du XML ci-dessous. Cela fait apparaître le code dans un modèle similaire à la sortie souhaitée.

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

crée

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

Tout le reste échoue, vous pouvez consulter cet article MSDN qui contient de nombreux exemples dont j'ai discuté ici et plus encore. http://msdn.microsoft.com/en-us/library/aa468556.aspx

Nyxtom
la source
3
Vous voudrez peut-être souligner que vous utilisez XDocument dans le dernier exemple car XDocument est assez différent de XmlDocument
Aaron Powell
2
Correction; il n'y a pas de C # 3.5; vous voulez dire .NET 3.5 et C # 3.0
Marc Gravell
oh, et le "à la volée" [initialiseurs d'objets] fonctionnerait en grande partie de la même manière avec C # 3.0 et XmlDocument - toujours une bonne réponse, cependant (+1)
Marc Gravell
Il vaut peut-être la peine de mentionner que si vous chargez un document à interroger avec XPath (et non à modifier), l'utilisation d'un XPathDocument sera beaucoup plus efficace
Oliver Hallam
Cette validation de schéma est-elle effectuée nœud par nœud? Sinon, y a-t-il un moyen de le faire nœud par nœud?
Malik Daud Ahmad Khokhar
30

Cela dépend de la taille; pour les xml de petite à moyenne taille, un DOM tel que XmlDocument (toutes les versions C # / .NET) ou XDocument (.NET 3.5 / C # 3.0) est le gagnant évident. Pour utiliser xsd, vous pouvez charger xml à l'aide d'un XmlReader , et un XmlReader accepte (pour créer ) un XmlReaderSettings . Les objets XmlReaderSettings ont une propriété Schemas qui peut être utilisée pour effectuer la validation xsd (ou dtd).

Pour l'écriture de XML, les mêmes choses s'appliquent, notant qu'il est un peu plus facile de mettre en page le contenu avec LINQ-to-XML (XDocument) qu'avec l'ancien XmlDocument.

Cependant, pour un XML énorme, un DOM peut chomp trop de mémoire, auquel cas vous devrez peut-être utiliser directement XmlReader / XmlWriter.

Enfin, pour manipuler xml, vous souhaiterez peut-être utiliser XslCompiledTransform (une couche xslt).

L'alternative au travail avec xml est de travailler avec un modèle objet; vous pouvez utiliser xsd.exe pour créer des classes qui représentent un modèle compatible xsd, charger simplement le xml en tant qu'objets , le manipuler avec OO, puis sérialiser à nouveau ces objets; vous faites cela avec XmlSerializer .

Marc Gravell
la source
Manipuler (ajouter / supprimer des éléments) un gros document XML (40k lignes). Quelle est la meilleure façon? J'avais l'habitude d'utiliser LINQ-to-XML.
Neyoh
12

La réponse de nyxtom est très bonne. J'ajouterais quelques choses à cela:

Si vous avez besoin d'un accès en lecture seule à un document XML, XPathDocumentest un objet beaucoup plus léger que XmlDocument.

L'inconvénient de l'utilisation XPathDocumentest que vous ne pouvez pas utiliser les méthodes SelectNodeset les SelectSingleNodeméthodes familières de XmlNode. À la place, vous devez utiliser les outils IXPathNavigablefournis par le: utilisez CreateNavigatorpour créer un XPathNavigator, et utilisez XPathNavigatorpour créer XPathNodeIterators pour parcourir les listes de nœuds que vous trouvez via XPath. Cela nécessite généralement quelques lignes de code de plus que les XmlDocumentméthodes.

Mais: les classes XmlDocumentet XmlNodeimplémentent IXPathNavigable, donc tout code que vous écrivez pour utiliser ces méthodes sur un XPathDocumentfonctionnera également sur un XmlDocument. Si vous vous habituez à écrire IXPathNavigable, vos méthodes peuvent fonctionner avec l'un ou l'autre des objets. (C'est pourquoi l'utilisation de XmlNodeet XmlDocumentdans les signatures de méthode est marquée par FxCop.)

Malheureusement, XDocumentet XElement(et XNodeet XObject) ne sont pas implémentés IXPathNavigable.

Une autre chose qui n'est pas présente dans la réponse de nyxtom est XmlReader. Vous utilisez généralement XmlReaderpour éviter la surcharge de l'analyse du flux XML dans un modèle objet avant de commencer à le traiter. Au lieu de cela, vous utilisez un XmlReaderpour traiter le flux d'entrée un nœud XML à la fois. C'est essentiellement la réponse de .NET à SAX. Il vous permet d'écrire du code très rapide pour le traitement de très gros documents XML.

XmlReader fournit également le moyen le plus simple de traiter des fragments de document XML, par exemple le flux d'éléments XML sans élément englobant renvoyé par l'option FOR XML RAW de SQL Server.

Le code que vous écrivez en utilisant XmlReaderest généralement très étroitement lié au format du XML qu'il lit. L'utilisation de XPath permet à votre code d'être beaucoup, beaucoup plus faiblement couplé au XML, c'est pourquoi c'est généralement la bonne réponse. Mais lorsque vous avez besoin de l'utiliser XmlReader, vous en avez vraiment besoin.

Robert Rossney
la source
3
Notez qu'il existe une méthode d'extension XPathNavigator CreateNavigator(this XNode node)pour créer un XPathNavigatorfrom an XNode(qui inclut la classe dérivée XDocument).
Dave
5

Tout d'abord, apprenez à connaître les nouvelles classes XDocument et XElement , car elles constituent une amélioration par rapport à la famille XmlDocument précédente.

  1. Ils travaillent avec LINQ
  2. Ils sont plus rapides et plus légers

Cependant , vous devrez peut-être continuer à utiliser les anciennes classes pour travailler avec le code hérité, en particulier les proxys générés précédemment. Dans ce cas, vous devrez vous familiariser avec certains modèles d'interopérabilité entre ces classes de gestion XML.

Je pense que votre question est assez large et exigerait trop de détails dans une seule réponse, mais c'est la première réponse générale à laquelle j'ai pensé et elle sert de point de départ.

hâte
la source
Je suis d'accord qu'ils (XDocument, etc.) sont excellents, mais l'OP a posé des questions sur C # 2.0.
Marc Gravell
2

Si vous travaillez sous .NET 3.5 et que vous n'avez pas peur du code expérimental, vous pouvez consulter LINQ to XSD ( http://blogs.msdn.com/xmlteam/archive/2008/02/21/linq-to- xsd-alpha-0-2.aspx ) qui générera des classes .NET à partir d'un XSD (y compris les règles intégrées du XSD).

Il a ensuite la possibilité d'écrire directement dans un fichier et de lire à partir d'un fichier en s'assurant qu'il est conforme aux règles XSD.

Je suggère vraiment d'avoir un XSD pour tout document XML avec lequel vous travaillez:

  • Vous permet d'appliquer des règles dans le XML
  • Permet aux autres de voir comment le XML est / sera structuré
  • Peut être utilisé pour la validation de XML

Je trouve que Liquid XML Studio est un excellent outil pour générer des XSD et c'est gratuit!

Aaron Powell
la source
2

Ecrire du XML avec la classe 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
la source
1

Si vous créez un ensemble de données typé dans le concepteur, vous obtenez automatiquement un xsd, un objet fortement typé, et pouvez charger et enregistrer le xml avec une seule ligne de code.

Peter C
la source
J'ai eu beaucoup de succès avec DataSet. Ils sont également très sympathiques avec les bases de données.
User1
1

Mon opinion personnelle, en tant que programmeur C #, est que la meilleure façon de gérer XML en C # est de déléguer cette partie du code à un projet VB .NET. Dans .NET 3.5, VB .NET a des littéraux XML, ce qui rend la gestion de XML beaucoup plus intuitive. Voir ici, par exemple:

Vue d'ensemble de LINQ to XML dans Visual Basic

(Assurez-vous de configurer la page pour afficher le code VB, pas le code C #.)

J'écrirais le reste du projet en C #, mais gérerais le XML dans un projet VB référencé.

Ryan Lundy
la source
Cela ne vaut pas la peine de passer en vb uniquement pour le littéral XML. XML ne traite que des littéraux. Si le xml est passé en tant que paramètre, la prise en charge du littéral XML ne vous offre pas beaucoup d'avantages. Au lieu de cela, la syntaxe héritée de vb.net ruinera l'heureuse expérience de programmation de C #.
Gqqnbig
0

nyxtom,

"Doc" et "xdoc" ne devraient-ils pas correspondre dans l'exemple 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
la source
J'ai soumis une modification pour approbation sur la réponse à laquelle vous faites référence, mais cela aurait vraiment dû être un commentaire, pas une réponse.
David Thompson
Merci David. D'accord, cela ne me permettrait pas de commenter à l'époque. Pas certain de pourquoi.
mokumaxCraig
0

La réponse de Cookey est bonne ... mais voici des instructions détaillées sur la façon de créer un objet fortement typé à partir d'un XSD (ou XML) et de sérialiser / désérialiser en quelques lignes de code:

Instructions

Steve Horn
la source
"La page que vous recherchez n'existe pas." :(
Ian Grainger
0

Si jamais vous avez besoin de convertir des données entre XmlNode<=> XNode<=> XElement
(par exemple pour utiliser LINQ), ces extensions peuvent vous être utiles:

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

Usage:

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
la source