valor recorrer parsear obtener nodos modificar manipular leer lector elementos cargar c# xml xml-serialization serialization

parsear - recorrer elementos de un xml c#



Cómo deserializar solo una parte de un documento XML en C# (9)

Aquí hay un ejemplo ficticio del problema que intento resolver. Si estoy trabajando en C #, y tengo XML como este:

<?xml version="1.0" encoding="utf-8"?> <Cars> <Car> <StockNumber>1020</StockNumber> <Make>Nissan</Make> <Model>Sentra</Model> </Car> <Car> <StockNumber>1010</StockNumber> <Make>Toyota</Make> <Model>Corolla</Model> </Car> <SalesPerson> <Company>Acme Sales</Company> <Position> <Salary> <Amount>1000</Amount> <Unit>Dollars</Unit> ... and on... and on.... </SalesPerson> </Cars>

el XML dentro de SalesPerson puede ser muy largo, con megabytes de tamaño. Quiero deserializar la etiqueta, pero no deserializar el elemento XML de SalesPerson en lugar de mantenerlo en forma cruda "para más adelante".

Esencialmente, me gustaría poder usar esto como una representación de Objetos del XML.

[System.Xml.Serialization.XmlRootAttribute("Cars", Namespace = "", IsNullable = false)] public class Cars { [XmlArrayItem(typeof(Car))] public Car[] Car { get; set; } public Stream SalesPerson { get; set; } } public class Car { [System.Xml.Serialization.XmlElementAttribute("StockNumber")] public string StockNumber{ get; set; } [System.Xml.Serialization.XmlElementAttribute("Make")] public string Make{ get; set; } [System.Xml.Serialization.XmlElementAttribute("Model")] public string Model{ get; set; } }

donde la propiedad SalesPerson en el objeto Cars contendría una secuencia con el xml sin formato que se encuentra dentro del elemento <SalesPerson> xml después de ejecutarse a través de un XmlSerializer.

Se puede hacer esto? ¿Puedo elegir deserializar solo "parte de" un documento xml?

¡Gracias! -Micro

ps ejemplo xml robado de Cómo deserializar documento XML


Creo que el comentario anterior es correcto en su comentario de que XML podría no ser la mejor opción de una tienda de respaldo aquí.

Si tiene problemas de escala y no aprovecha algunas de las otras sutilezas que obtiene con XML, como las transformaciones, es mejor que utilice una base de datos para sus datos. Las operaciones que estás haciendo realmente parecen encajar más en ese modelo.

Sé que esto realmente no responde a tu pregunta, pero pensé que destacaría una solución alternativa que podrías usar. Una buena base de datos y un correlacionador OR apropiado como .netTiers, NHibernate o, más recientemente, LINQ to SQL / Entity Framework probablemente lo pondrán de nuevo en funcionamiento con cambios mínimos en el resto de su base de código.


Intente definir la propiedad SalesPerson como tipo XmlElement . Esto funciona para los resultados de los servicios web ASMX, que utilizan la serialización XML. Yo pensaría que también funcionaría en la entrada. Esperaría que todo el elemento <SalesPerson> en XmlElement .


La answer aceptada de user271807 es una gran solución, pero descubrí que también necesitaba establecer la raíz xml del fragmento para evitar una excepción con una excepción interna que dijera algo como esto:

...xmlns=''''> was not expected

Esta excepción se inició cuando intenté deserializar solo el elemento interno de Autenticación de este documento xml:

<?xml version=""1.0"" encoding=""UTF-8""?> <Api> <Authentication> <sessionid>xxx</sessionid> <errormessage>xxx</errormessage> </Authentication> </ApI>

Así que terminé creando este método de extensión como una solución reutilizable : la advertencia contiene una pérdida de memoria, ver a continuación:

public static T DeserializeXml<T>(this string @this, string innerStartTag = null) { using (var stringReader = new StringReader(@this)) using (var xmlReader = XmlReader.Create(stringReader)) { if (innerStartTag != null) { xmlReader.ReadToDescendant(innerStartTag); var xmlSerializer = new XmlSerializer(typeof(T), new XmlRootAttribute(innerStartTag)); return (T)xmlSerializer.Deserialize(xmlReader.ReadSubtree()); } return (T)new XmlSerializer(typeof(T)).Deserialize(xmlReader); } }

Actualización 20 de marzo de 2017: Como se señala en el comentario a continuación, hay un problema de pérdida de memoria al utilizar uno de los constructores de XmlSerializer, así que terminé usando una solución de almacenamiento en caché como se muestra a continuación:

/// <summary> /// Deserialize XML string, optionally only an inner fragment of the XML, as specified by the innerStartTag parameter. /// </summary> public static T DeserializeXml<T>(this string @this, string innerStartTag = null) { using (var stringReader = new StringReader(@this)) { using (var xmlReader = XmlReader.Create(stringReader)) { if (innerStartTag != null) { xmlReader.ReadToDescendant(innerStartTag); var xmlSerializer = CachingXmlSerializerFactory.Create(typeof (T), new XmlRootAttribute(innerStartTag)); return (T) xmlSerializer.Deserialize(xmlReader.ReadSubtree()); } return (T) CachingXmlSerializerFactory.Create(typeof (T), new XmlRootAttribute("AutochartistAPI")).Deserialize(xmlReader); } } } /// <summary> /// A caching factory to avoid memory leaks in the XmlSerializer class. /// See http://dotnetcodebox.blogspot.dk/2013/01/xmlserializer-class-may-result-in.html /// </summary> public static class CachingXmlSerializerFactory { private static readonly ConcurrentDictionary<string, XmlSerializer> Cache = new ConcurrentDictionary<string, XmlSerializer>(); public static XmlSerializer Create(Type type, XmlRootAttribute root) { if (type == null) { throw new ArgumentNullException(nameof(type)); } if (root == null) { throw new ArgumentNullException(nameof(root)); } var key = string.Format(CultureInfo.InvariantCulture, "{0}:{1}", type, root.ElementName); return Cache.GetOrAdd(key, _ => new XmlSerializer(type, root)); } public static XmlSerializer Create<T>(XmlRootAttribute root) { return Create(typeof (T), root); } public static XmlSerializer Create<T>() { return Create(typeof (T)); } public static XmlSerializer Create<T>(string defaultNamespace) { return Create(typeof (T), defaultNamespace); } public static XmlSerializer Create(Type type) { return new XmlSerializer(type); } public static XmlSerializer Create(Type type, string defaultNamespace) { return new XmlSerializer(type, defaultNamespace); } }


Le sugiero que lea manualmente desde Xml, utilizando cualquier método liviano, como XmlReader, XPathDocument o LINQ-to-XML.

Cuando tiene que leer solo 3 propiedades, supongo que puede escribir código que lea manualmente desde ese nodo y tenga un control total de cómo se ejecuta en lugar de confiar en la serialización / deserialización.


Normalmente, la deserialización de XML es una proposición de todo o nada, por lo que probablemente necesite personalizarla. Si no realiza una deserialización completa, corre el riesgo de que el xml esté mal formado dentro del elemento SalesPerson, por lo que el documento no es válido.

Si está dispuesto a aceptar ese riesgo, probablemente quiera hacer un análisis de texto básico para dividir los elementos de SalesPerson en un documento diferente utilizando las herramientas de procesamiento de texto plano, y luego procesar el XML.

Este es un buen ejemplo de por qué XML no siempre es la respuesta correcta.


Puede controlar cómo se realiza su serialización implementando la interfaz ISerializable en su clase. Tenga en cuenta que esto también implicará un constructor con la firma del método (SerializationInfoInfo, StreamingContext context) y seguro de que puede hacer lo que está pidiendo con eso.

Sin embargo, mire detenidamente si realmente necesita hacer esto con la transmisión porque si no tiene que usar el mecanismo de transmisión, será más fácil lograr lo mismo con Linq a XML y, a la larga, será más fácil de mantener. término (IMO)


Puede controlar qué partes de la clase Cars se deserializan implementando la interfaz IXmlSerializable en la clase Cars, y luego, en el método ReadXml (XmlReader) leería y deserializaría los elementos Car pero cuando llegue al elemento SalesPerson leería su subárbol como una cadena y luego construir una secuencia sobre el contenido de texto utilizando un StreamWriter.

Si nunca desea que XmlSerializer escriba el elemento SalesPerson, use el atributo [XmlIgnore]. No estoy seguro de lo que quiere que suceda cuando se serializa la clase Cars a su representación XML. ¿Está tratando de evitar solo la deserialización de SalesPerson mientras aún puede serializar la representación XML de SalesPerson representada por Stream?

Probablemente podría proporcionar un ejemplo de código de esto si quieres una implementación concreta.


Puede ser un hilo un poco viejo, pero lo publicaré de todos modos. tuve el mismo problema (necesité deserializar como 10kb de datos de un archivo que tenía más de 1MB). En el objeto principal (que tiene un InnerObject que debe ser deserializador) implementé una interfaz IXmlSerializable, luego cambié el método ReadXml.

Tenemos xmlTextReader como entrada, la primera línea es para leer hasta una etiqueta XML:

reader.ReadToDescendant("InnerObjectTag"); //tag which matches the InnerObject

Luego crea XMLSerializer para un tipo de objeto que queremos deserializar y deserializar

XmlSerializer serializer = new XmlSerializer(typeof(InnerObject)); this.innerObject = serializer.Deserialize(reader.ReadSubtree()); //this gives serializer the part of XML that is for the innerObject data reader.close(); //now skip the rest

esto me ahorró mucho tiempo para deserializar y me permite leer solo una parte de XML (solo algunos detalles que describen el archivo, que pueden ayudar al usuario a decidir si el archivo es lo que quiere cargar).


Si todo lo que quiere hacer es analizar el elemento SalesPerson pero mantenerlo como una cadena, debe usar Xsl Transform en lugar de "Deserialization". Si, por otro lado, desea analizar el elemento SalesPerson y solo rellenar un objeto en la memoria de todos los demás elementos no SalesPerson, Xsl Transform también podría ser el camino a seguir. Si los archivos son demasiado grandes, puede considerar separarlos y usar Xsl para combinar diferentes archivos xml para que SalesPerson I / O solo se produzca cuando lo necesite.