Cómo lidiar con XML en C #

85

¿Cuál es la mejor manera de tratar con documentos XML, XSD, etc. en C # 2.0?

Qué clases usar, etc. ¿Cuáles son las mejores prácticas para analizar y crear documentos XML, etc.?

EDITAR: Las sugerencias de .Net 3.5 también son bienvenidas.

Malik Daud Ahmad Khokhar
fuente
Para aquellos que también intentan encontrar una solución más viable, ignoren esto. Es una antigua biblioteca .NET. En su lugar, use XDocument y se ahorrará arrancarse los ojos de frustración
AER

Respuestas:

177

El medio principal de lectura y escritura en C # 2.0 se realiza a través de la clase XmlDocument . Puede cargar la mayoría de sus configuraciones directamente en XmlDocument a través del XmlReader que acepta.

Cargando XML directamente

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

Cargar XML desde un archivo

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

Encuentro que la forma más fácil / rápida de leer un documento XML es utilizando XPath.

Leer un documento XML usando XPath (usando XmlDocument que nos permite editar)

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 necesita trabajar con documentos XSD para validar un documento XML, puede usar esto.

Validación de documentos XML con esquemas 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); }

Validación de XML contra XSD en cada nodo (ACTUALIZACIÓN 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
}

Escribir un documento XML (manualmente)

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

(ACTUALIZACIÓN 1)

En .NET 3.5, usa XDocument para realizar tareas similares. Sin embargo, la diferencia es que tiene la ventaja de realizar consultas Linq para seleccionar los datos exactos que necesita. Con la adición de inicializadores de objetos, puede crear una consulta que incluso devuelva objetos de su propia definición directamente en la consulta.

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

(ACTUALIZACIÓN 2)

Una buena forma en .NET 3.5 es usar XDocument para crear XML a continuación. Esto hace que el código aparezca en un patrón similar al resultado deseado.

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

crea

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

Todo lo demás falla, puede consultar este artículo de MSDN que tiene muchos ejemplos que he discutido aquí y más. http://msdn.microsoft.com/en-us/library/aa468556.aspx

nyxtom
fuente
3
Es posible que desee señalar que está utilizando XDocument en el último ejemplo, ya que XDocument es bastante diferente a XmlDocument
Aaron Powell
2
Corrección; no hay C # 3.5; te refieres a .NET 3.5 y C # 3.0
Marc Gravell
Ah, y los [inicializadores de objetos] "sobre la marcha" funcionarían prácticamente igual con C # 3.0 y XmlDocument; sin embargo, sigue siendo una buena respuesta (+1)
Marc Gravell
Vale la pena mencionar que si está cargando un documento para consultarlo con XPath (y no para editar), entonces usar un XPathDocument será mucho más eficiente
Oliver Hallam
¿Esta validación de esquema se realiza nodo por nodo? Si no es así, ¿hay alguna forma de hacerlo nodo por nodo?
Malik Daud Ahmad Khokhar
30

Depende del tamaño; para xml de tamaño pequeño a mediano, un DOM como XmlDocument (cualquier versión de C # / .NET) o XDocument (.NET 3.5 / C # 3.0) es el ganador obvio. Para usar xsd, puede cargar xml usando un XmlReader , y un XmlReader acepta (para crear ) un XmlReaderSettings . Los objetos XmlReaderSettings tienen una propiedad Schemas que se puede usar para realizar la validación xsd (o dtd).

Para escribir xml, se aplican las mismas cosas, teniendo en cuenta que es un poco más fácil diseñar contenido con LINQ-to-XML (XDocument) que con XmlDocument anterior.

Sin embargo, para xml de gran tamaño, un DOM puede consumir demasiada memoria, en cuyo caso es posible que deba usar XmlReader / XmlWriter directamente.

Finalmente, para manipular xml, es posible que desee utilizar XslCompiledTransform (una capa xslt).

La alternativa a trabajar con xml es trabajar con un modelo de objetos; puede usar xsd.exe para crear clases que representen un modelo compatible con xsd, y simplemente cargar el xml como objetos , manipularlo con OO y luego serializar esos objetos nuevamente; haces esto con XmlSerializer .

Marc Gravell
fuente
Manipular (agregar / suministrar elementos) un documento XML grande (40k líneas). Cual es la mejor manera? Solía ​​usar LINQ-to-XML.
Neyoh
12

La respuesta de nyxtom es muy buena. Le agregaría un par de cosas:

Si necesita acceso de solo lectura a un documento XML, XPathDocumentes un objeto mucho más liviano que XmlDocument.

La desventaja de usar XPathDocumentes que no se puede utilizar el familiar SelectNodesy SelectSingleNodemétodos de XmlNode. En su lugar, debe usar las herramientas que IXPathNavigableproporciona: use CreateNavigatorpara crear un XPathNavigator, y use XPathNavigatorpara crear XPathNodeIterators para iterar sobre las listas de nodos que encuentre a través de XPath. Esto generalmente requiere algunas líneas más de código que los XmlDocumentmétodos.

Pero: las clases XmlDocumenty XmlNodese implementan IXPathNavigable, por lo que cualquier código que escriba para usar esos métodos en un XPathDocumenttambién funcionará en un XmlDocument. Si te acostumbras a escribir en contra IXPathNavigable, tus métodos pueden funcionar contra cualquier objeto. (Esta es la razón por la que FxCop marca el uso de XmlNodey XmlDocumenten las firmas de métodos).

Lamentablemente, XDocumenty XElement( XNodey XObject) no se implementan IXPathNavigable.

Otra cosa que no está presente en la respuesta de nyxtom es XmlReader. Por lo general, se usa XmlReaderpara evitar la sobrecarga de analizar el flujo XML en un modelo de objeto antes de comenzar a procesarlo. En su lugar, utiliza un XmlReaderpara procesar el flujo de entrada un nodo XML a la vez. Esta es esencialmente la respuesta de .NET a SAX. Le permite escribir código muy rápido para procesar documentos XML muy grandes.

XmlReader también proporciona la forma más sencilla de procesar fragmentos de documentos XML, por ejemplo, el flujo de elementos XML sin ningún elemento adjunto que devuelve la opción FOR XML RAW de SQL Server.

El código que escribe utilizando XmlReadergeneralmente está muy estrechamente acoplado al formato del XML que está leyendo. El uso de XPath permite que su código esté mucho más acoplado al XML, por lo que generalmente es la respuesta correcta. Pero cuando lo necesita XmlReader, realmente lo necesita.

Robert Rossney
fuente
3
Tenga en cuenta que existe un método de extensión XPathNavigator CreateNavigator(this XNode node)para crear un XPathNavigatordesde un XNode(que incluye la clase derivada XDocument).
Dave
5

En primer lugar, conozca las nuevas clases XDocument y XElement , porque son una mejora con respecto a la familia XmlDocument anterior.

  1. Trabajan con LINQ
  2. Son más rápidos y ligeros

Sin embargo , es posible que deba seguir usando las clases antiguas para trabajar con código heredado, en particular los proxies generados anteriormente. En ese caso, deberá familiarizarse con algunos patrones para interoperar entre estas clases de manejo de XML.

Creo que su pregunta es bastante amplia y requeriría demasiado en una sola respuesta para dar detalles, pero esta es la primera respuesta general en la que pensé y sirve como un comienzo.

prisa
fuente
Estoy de acuerdo en que (XDocument, etc.) son geniales, pero el OP preguntó sobre C # 2.0.
Marc Gravell
2

Si está trabajando en .NET 3.5 y no le teme al código experimental, puedes consultar LINQ to XSD ( http://blogs.msdn.com/xmlteam/archive/2008/02/21/linq-to- xsd-alpha-0-2.aspx ) que generará clases .NET a partir de un XSD (incluidas las reglas integradas del XSD).

Luego tiene la capacidad de escribir directamente en un archivo y leer desde un archivo, asegurándose de que cumpla con las reglas XSD.

Definitivamente sugiero tener un XSD para cualquier documento XML con el que trabaje:

  • Le permite hacer cumplir las reglas en XML
  • Permite que otros vean cómo está / estará estructurado el XML
  • Puede utilizarse para la validación de XML

¡Encuentro que Liquid XML Studio es una gran herramienta para generar XSD y es gratis!

Aaron Powell
fuente
2

Escribir XML con la clase 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
fuente
1

Si crea un conjunto de datos escrito en el diseñador, automáticamente obtiene un xsd, un objeto fuertemente tipado, y puede cargar y guardar el xml con una línea de código.

Peter C
fuente
He tenido un gran éxito con DataSet's. También son muy amigables con las bases de datos.
Usuario1
1

Mi opinión personal, como programador de C #, es que la mejor manera de tratar con XML en C # es delegar esa parte del código a un proyecto de VB .NET. En .NET 3.5, VB .NET tiene XML Literals, lo que hace que el manejo de XML sea mucho más intuitivo. Vea aquí, por ejemplo:

Descripción general de LINQ to XML en Visual Basic

(Asegúrese de configurar la página para que muestre el código VB, no el código C #).

Escribiría el resto del proyecto en C #, pero manejaría el XML en un proyecto VB referenciado.

Ryan Lundy
fuente
No vale la pena cambiar a vb solo para XML literal. XML solo se ocupa de literales. Si el xml se pasa como parámetro, el soporte literal XML no le brinda muchos beneficios. En cambio, la sintaxis heredada de vb.net arruinará la feliz experiencia de programación de C #.
Gqqnbig
0

nyxtom,

¿No deberían coincidir "doc" y "xdoc" en el Ejemplo 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
fuente
Envié una edición para aprobación de la respuesta a la que se refiere, sin embargo, esto realmente debería haber sido un comentario, no una respuesta.
David Thompson
Gracias David. De acuerdo, no me permitiría comentar en ese momento. No estoy seguro de por qué.
mokumaxCraig
0

La respuesta de Cookey es buena ... pero aquí hay instrucciones detalladas sobre cómo crear un objeto fuertemente tipado a partir de un XSD (o XML) y serializar / deserializar en unas pocas líneas de código:

Instrucciones

Steve Horn
fuente
"La página que estaba buscando no existe". :(
Ian Grainger
0

Si alguna vez necesita convertir datos entre XmlNode<=> XNode<=> XElement
(por ejemplo, para usar LINQ), estas Extensiones pueden ser útiles para usted:

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

Uso:

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
fuente