read - xml to list object c#
¿Cuál es la mejor manera de analizar XML(grande) en C#Code? (5)
Asat 14 de mayo de 2009: He cambiado a usar un enfoque híbrido ... ver código a continuación.
Esta versión tiene la mayoría de las ventajas de ambos:
* el XmlReader / XmlTextReader (eficiencia de la memoria -> velocidad); y
* el XmlSerializer (code-gen -> desarrollo rápido y flexibilidad).
Utiliza el XmlTextReader para recorrer el documento y crea "doclets" que se deserializa utilizando las clases XmlSerializer y "XML binding" generadas con XSD.EXE.
Supongo que esta receta es universalmente aplicable, y es rápida ... Estoy analizando un documento XML de 201 MB que contiene 56,000 características GML en aproximadamente 7 segundos ... la implementación antigua de VB6 de esta aplicación tardó minutos (o incluso horas) en analizar grandes extractos ... así que me veo bien para irme.
Una vez más, un GRAN agradecimiento a los foros por donar su valioso tiempo. Realmente lo aprecio.
Saludos a todos. Keith.
using System;
using System.Reflection;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
using System.Collections.Generic;
using nrw_rime_extract.utils;
using nrw_rime_extract.xml.generated_bindings;
namespace nrw_rime_extract.xml
{
internal interface ExtractXmlReader
{
rimeType read(string xmlFilename);
}
/// <summary>
/// RimeExtractXml provides bindings to the RIME Extract XML as defined by
/// $/Release 2.7/Documentation/Technical/SCHEMA and DTDs/nrw-rime-extract.xsd
/// </summary>
internal class ExtractXmlReader_XmlSerializerImpl : ExtractXmlReader
{
private Log log = Log.getInstance();
public rimeType read(string xmlFilename)
{
log.write(
string.Format(
"DEBUG: ExtractXmlReader_XmlSerializerImpl.read({0})",
xmlFilename));
using (Stream stream = new FileStream(xmlFilename, FileMode.Open))
{
return read(stream);
}
}
internal rimeType read(Stream xmlInputStream)
{
// create an instance of the XmlSerializer class,
// specifying the type of object to be deserialized.
XmlSerializer serializer = new XmlSerializer(typeof(rimeType));
serializer.UnknownNode += new XmlNodeEventHandler(handleUnknownNode);
serializer.UnknownAttribute +=
new XmlAttributeEventHandler(handleUnknownAttribute);
// use the Deserialize method to restore the object''s state
// with data from the XML document.
return (rimeType)serializer.Deserialize(xmlInputStream);
}
protected void handleUnknownNode(object sender, XmlNodeEventArgs e)
{
log.write(
string.Format(
"XML_ERROR: Unknown Node at line {0} position {1} : {2}/t{3}",
e.LineNumber, e.LinePosition, e.Name, e.Text));
}
protected void handleUnknownAttribute(object sender, XmlAttributeEventArgs e)
{
log.write(
string.Format(
"XML_ERROR: Unknown Attribute at line {0} position {1} : {2}=''{3}''",
e.LineNumber, e.LinePosition, e.Attr.Name, e.Attr.Value));
}
}
/// <summary>
/// xtractXmlReader provides bindings to the extract.xml
/// returned by the RIME server; as defined by:
/// $/Release X/Documentation/Technical/SCHEMA and
/// DTDs/nrw-rime-extract.xsd
/// </summary>
internal class ExtractXmlReader_XmlTextReaderXmlSerializerHybridImpl :
ExtractXmlReader
{
private Log log = Log.getInstance();
public rimeType read(string xmlFilename)
{
log.write(
string.Format(
"DEBUG: ExtractXmlReader_XmlTextReaderXmlSerializerHybridImpl." +
"read({0})",
xmlFilename));
using (XmlReader reader = XmlReader.Create(xmlFilename))
{
return read(reader);
}
}
public rimeType read(XmlReader reader)
{
rimeType result = new rimeType();
// a deserializer for featureClass, feature, etc, "doclets"
Dictionary<Type, XmlSerializer> serializers =
new Dictionary<Type, XmlSerializer>();
serializers.Add(typeof(featureClassType),
newSerializer(typeof(featureClassType)));
serializers.Add(typeof(featureType),
newSerializer(typeof(featureType)));
List<featureClassType> featureClasses = new List<featureClassType>();
List<featureType> features = new List<featureType>();
while (!reader.EOF)
{
if (reader.MoveToContent() != XmlNodeType.Element)
{
reader.Read(); // skip non-element-nodes and unknown-elements.
continue;
}
// skip junk nodes.
if (reader.Name.Equals("featureClass"))
{
using (
StringReader elementReader =
new StringReader(reader.ReadOuterXml()))
{
XmlSerializer deserializer =
serializers[typeof (featureClassType)];
featureClasses.Add(
(featureClassType)
deserializer.Deserialize(elementReader));
}
continue;
// ReadOuterXml advances the reader, so don''t read again.
}
if (reader.Name.Equals("feature"))
{
using (
StringReader elementReader =
new StringReader(reader.ReadOuterXml()))
{
XmlSerializer deserializer =
serializers[typeof (featureType)];
features.Add(
(featureType)
deserializer.Deserialize(elementReader));
}
continue;
// ReadOuterXml advances the reader, so don''t read again.
}
log.write(
"WARNING: unknown element ''" + reader.Name +
"'' was skipped during parsing.");
reader.Read(); // skip non-element-nodes and unknown-elements.
}
result.featureClasses = featureClasses.ToArray();
result.features = features.ToArray();
return result;
}
private XmlSerializer newSerializer(Type elementType)
{
XmlSerializer serializer = new XmlSerializer(elementType);
serializer.UnknownNode += new XmlNodeEventHandler(handleUnknownNode);
serializer.UnknownAttribute +=
new XmlAttributeEventHandler(handleUnknownAttribute);
return serializer;
}
protected void handleUnknownNode(object sender, XmlNodeEventArgs e)
{
log.write(
string.Format(
"XML_ERROR: Unknown Node at line {0} position {1} : {2}/t{3}",
e.LineNumber, e.LinePosition, e.Name, e.Text));
}
protected void handleUnknownAttribute(object sender, XmlAttributeEventArgs e)
{
log.write(
string.Format(
"XML_ERROR: Unknown Attribute at line {0} position {1} : {2}=''{3}''",
e.LineNumber, e.LinePosition, e.Attr.Name, e.Attr.Value));
}
}
}
Estoy escribiendo una herramienta de cliente GIS en C # para recuperar "características" en un esquema XML basado en GML (ejemplo a continuación) desde un servidor. Los extractos están limitados a 100,000 características.
Considero que el archivo extract.xml más grande podría obtener alrededor de 150 megabytes, por lo que, obviamente, los analizadores DOM están fuera. He estado tratando de decidir entre XmlSerializer y XSD.EXE enlaces generados --OR-- XmlReader y un gráfico de objetos hechos a mano.
¿O tal vez hay una mejor manera que no he considerado todavía? Como XLINQ, o ????
Por favor, ¿alguien puede guiarme? Especialmente con respecto a la eficiencia de memoria de cualquier enfoque dado. Si no, tendré que hacer un "prototipo" de ambas soluciones y perfilarlas una al lado de la otra.
Soy un gambón crudo en .NET. Cualquier orientación sería muy apreciada.
Agradeciendote. Keith.
XML de ejemplo : hasta 100,000 de ellos, de hasta 234,600 coords por función.
<feature featId="27168306" fType="vegetation" fTypeId="1129" fClass="vegetation" gType="Polygon" ID="0" cLockNr="51598" metadataId="51599" mdFileId="NRM/TIS/VEGETATION/9543_22_v3" dataScale="25000">
<MultiGeometry>
<geometryMember>
<Polygon>
<outerBoundaryIs>
<LinearRing>
<coordinates>153.505004,-27.42196 153.505044,-27.422015 153.503992 .... 172 coordinates omitted to save space ... 153.505004,-27.42196</coordinates>
</LinearRing>
</outerBoundaryIs>
</Polygon>
</geometryMember>
</MultiGeometry>
</feature>
Solo para resumir y hacer que la respuesta sea un poco más obvia para cualquiera que encuentre este hilo en google.
Antes de .NET 2, el XmlTextReader era el analizador XML con mayor capacidad de memoria disponible en la API estándar (gracias, Mitch ;-)
.NET 2 introdujo la clase XmlReader, que es mejor de nuevo. Es un iterador de elementos de solo reenvío (un poco como un analizador StAX). (gracias Cerebrus ;-)
Y recuerde niños, de cualquier instancia XML tiene el potencial de ser más grande que aproximadamente 500k, ¡NO USE DOM!
Saludos a todos. Keith.
Solo quería agregar este método de extensión simple como un ejemplo del uso de XmlReader (como Mitch respondió):
public static bool SkipToElement (this XmlReader xmlReader, string elementName)
{
if (!xmlReader.Read ())
return false;
while (!xmlReader.EOF)
{
if (xmlReader.NodeType == XmlNodeType.Element && xmlReader.Name == elementName)
return true;
xmlReader.Skip ();
}
return false;
}
Y el uso:
using (var xml_reader = XmlReader.Create (this.source.Url))
{
if (!SkipToElement (xml_reader, "Root"))
throw new InvalidOperationException ("XML element /"Root/" was not found.");
if (!SkipToElement (xml_reader, "Users"))
throw new InvalidOperationException ("XML element /"Root/Users/" was not found.");
...
}
Un analizador SAX puede ser lo que estás buscando. SAX no requiere que lea todo el documento en la memoria, lo analiza gradualmente y le permite procesar los elementos sobre la marcha. No sé si se proporciona un analizador de SAX en .NET, pero hay algunas opciones de código abierto que se pueden ver:
- http://saxdotnet.sourceforge.net/
- http://www.codeguru.com/csharp/csharp/cs_data/xml/article.php/c4221
Aquí hay una publicación relacionada:
Use XmlReader
para analizar documentos XML grandes. XmlReader
proporciona XmlReader
rápido, directo y sin almacenamiento en caché a datos XML. (Reenviar solo significa que puede leer el archivo XML de principio a fin, pero no puede retroceder en el archivo). XmlReader
usa pequeñas cantidades de memoria, y es equivalente a usar un simple lector SAX.
using (XmlReader myReader = XmlReader.Create(@"c:/data/coords.xml"))
{
while (myReader.Read())
{
// Process each node (myReader.Value) here
// ...
}
}
Puede usar XmlReader para procesar archivos de hasta 2 gigabytes (GB) de tamaño.