Как создать XmlNodes из XmlReader

Я разбираю большое количество больших файлов и после профилирования моего узкого места:

XmlDocument doc = new XmlDocument();
doc.Load(filename);

Этот подход был очень удобен, потому что я мог извлечь такие узлы:

XmlNodeList nodeList = doc.SelectNodes("myXPath");

Я перехожу к XmlReader, но когда я нахожу элемент, который мне нужно извлечь, я зацикливаюсь на том, как построить из него XmlNode, не знакомый с XmlReader:

XmlReader xmlReader = XmlReader.Create(fileName);

while (xmlReader.Read())
{
   //keep reading until we see my element
   if (xmlReader.Name.Equals("myElementName") && (xmlReader.NodeType == XmlNodeType.Element))
   {
       // How do I get the Xml element from the reader here?
   }
}

Я хотел бы создать объект List<XmlNode>. Я нахожусь на .NET 2.0.

Любая помощь оценивается!

Ответы

Ответ 1

Тип XmlNode не имеет общего конструктора, поэтому вы не можете создавать его самостоятельно. Вам нужно будет создать XmlDocument, который вы можете использовать для их создания:

XmlDocument doc = new XmlDocument();
while (xmlReader.Read())
{
    //keep reading until we see my element
    if (xmlReader.Name.Equals("myElementName") && (xmlReader.NodeType == XmlNodeType.Element))
    {
        // How do I get the Xml element from the reader here?
        XmlNode myNode = doc.CreateNode(XmlNodeType.Element, xmlReader.Name, "");
        nodeList.Add(myNode);
    }        
}

Ответ 2

Почему бы просто не сделать следующее?

XmlDocument doc = new XmlDocument();
XmlNode node = doc.ReadNode(reader);

Ответ 3

XmlReader и XmlDocument имеют очень четкий способ обработки. XmlReader не хранит ничего в памяти и использует подход только вперед, а не создание полного дерева DOM в памяти для XmlDocument. Это полезно, когда производительность является проблемой, но также требует, чтобы вы писали свое приложение по-другому: вместо использования XmlNode вы ничего не держите и только обрабатываете "в пути": т.е. Когда элемент проходит мимо этого вам нужно, вы что-то делаете. Это близко к подходу SAX, но без модели обратного вызова.

Ответ на вопрос "как получить XmlElement" заключается в следующем: вам придется создавать их с нуля на основе информации от читателя. Это, к сожалению, не соответствует повышению производительности. Часто лучше избегать использования подходов DOM вообще после переключения на XmlReader, если только для нескольких отдельных случаев.

Кроме того, "очень удобный" способ извлечения узлов с использованием XPath (SelectNodes - это то, что вы показываете выше) не может быть использован здесь: XPath требует дерева DOM. Подумайте об этом подходе к фильтрации: вы можете добавить фильтры в XmlReader и сказать ему пропустить определенные узлы или прочитать до определенного node. Это очень быстро, но отличается по-другому.

Ответ 4

Используйте XmlDocument.ReadNode для этого подхода. Поместите XmlReader в инструкцию using и используйте XmlReader.LocalName вместо имени, чтобы удалить префикс пространства имен.

Ответ 5

Я использовал следующее обходное решение, когда мне пришлось вставлять данные из XmlReader в XmlDocumenht:

XmlReader rdr = cmd.ExecuteXmlReader();

XmlDocument doc = new XmlDocument();

// create a container node for our resultset
XmlElement root = doc.CreateElement("QueryRoot");
doc.AppendChild(root);

StringBuilder xmlBody = new StringBuilder();

while(rdr.Read())
{
    xmlBody.Append(rdr.ReadOuterXml());
}

root.InnerXml = xmlBody.ToString();

Ответ 6

Вот мой подход:

public static IEnumerable<XmlNode> StreamNodes(
    string path,
    string[] tagNames) 
{            
    var doc = new XmlDocument();            
    using (XmlReader xr = XmlReader.Create(path)) 
    {
        xr.MoveToContent();
        while (true) {
            if (xr.NodeType == XmlNodeType.Element &&
                tagNames.Contains(xr.Name)) 
            {
                var node = doc.ReadNode(xr);
                yield return node;
            } 
            else 
            {
                if (!xr.Read()) 
                {
                    break;
                }
            }
        }
        xr.Close();
    }                        
}
// Used like this:
foreach (var el in StreamNodes("orders.xml", new string[]{"order"})) 
{
    ....
}

Затем узлы могут быть импортированы в другой документ для дальнейшей обработки.