Самый эффективный способ заменить текст в потоке xml

У меня есть огромный кусок XML-данных, которые мне нужно «очистить». Xml выглядит примерно так:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
    <w:body>
        <w:p>       
                    <w:t>F_ck</w:t>
            <!-- -->
                <w:t>F_ck</w:t>
            <!-- -->
                            <w:t>F_ck</w:t>
        </w:p>
    </w:body>
</w:document>

Я хотел бы идентифицировать элементы <w:t> со значением «F_ck» и заменить это значение чем-то другим. Элементы, которые мне нужно очистить, будут разбросаны по всему документу.

Мне нужно, чтобы код работал как можно быстрее и занимал как можно меньше памяти, поэтому я не хочу использовать подходы XDocument (DOM), которые я нашел здесь и в других местах.

Данные передаются мне в виде потока, содержащего данные Xml, и моя интуиция подсказывает мне, что мне нужны XmlTextReader и XmlTextWriter.

Моя первоначальная идея заключалась в том, чтобы работать в режиме SAX, прогонять данные Xml только вперед и «передавать» их в XmlTextWriter, но я не могу найти разумный способ сделать это.

Я написал этот код:

var reader = new StringReader(content);
var xmltextReader = new XmlTextReader(reader);
var memStream = new MemoryStream();
var xmlWriter = new XmlTextWriter(memStream, Encoding.UTF8);

while (xmltextReader.Read())
{
    if (xmltextReader.Name == "w:t")
    {
        //xmlWriter.WriteRaw("blah");
    }
    else
    {
        xmlWriter.WriteRaw(xmltextReader.Value);
    }
}

Приведенный выше код принимает только значение объявления элементов и т. д., поэтому никаких скобок или чего-либо еще. Я понимаю, что мог бы написать код, который конкретно выполнял бы .WriteElement(), .WriteEndElement() и т. д. в зависимости от NodeType, но боюсь, что это быстро приведет к беспорядку.

Итак, вопрос:

Как мне - по-хорошему - передать данные xml, считанные из XmlTextReader, в XmlTextWriter, сохраняя при этом возможность манипулировать данными во время передачи?


person Jesper Lund Stocholm    schedule 05.11.2015    source источник
comment
«w» называется префиксом и определяется пространством имен: xmlns:w=schemas.openxmlformats .org/wordprocessingml/2006/main. Что ты пытаешься сделать? Документ не нужно очищать для десериализации.   -  person jdweng    schedule 05.11.2015
comment
@jdweng Я знаю, что такое пространство имен :-). Я не пытаюсь решить десериализацию. Я пытаюсь найти лучший способ заменить значения определенных элементов в данных Xml.   -  person Jesper Lund Stocholm    schedule 05.11.2015
comment
Используйте XDocument (xml linq). Найдите теги, а затем просто замените значения.   -  person jdweng    schedule 05.11.2015
comment
@jdweng Да, я посмотрю, смогу ли я заставить его работать, но, как я писал в ОП, я не хочу использовать XDocument из-за его объема памяти. Поэтому я держу сообщение открытым в надежде, что вместо этого смогу получить помощь по использованию XmlTextReader/Writer :-)   -  person Jesper Lund Stocholm    schedule 05.11.2015
comment
Если вас беспокоит скорость или память, попробуйте код на следующем веб-сайте. Это XML-файл размером 6 МБ, который запускается за пару секунд, если вы загружаете XML-файл на локальный диск. stackoverflow.com/questions/33506815/   -  person jdweng    schedule 06.11.2015


Ответы (1)


Попробуй это

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string xml =
                "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" +
                "<w:document xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\">" +
                    "<w:body>" +
                        "<w:p>" +
                                    "<w:t>F_ck</w:t>" +
                            "<!-- -->" +
                                "<w:t>F_ck</w:t>" +
                            "<!-- -->" +
                                            "<w:t>F_ck</w:t>" +
                        "</w:p>" +
                    "</w:body>" +
                "</w:document>";

            XDocument doc = XDocument.Parse(xml);
            XElement document = (XElement)doc.FirstNode;
            XNamespace ns_w = document.GetNamespaceOfPrefix("w");
            List<XElement> ts = doc.Descendants(ns_w + "t").ToList();
            foreach (XElement t in ts)
            {
                t.Value = "abc";
            }

        }
    }
}
​

person jdweng    schedule 05.11.2015
comment
Почему вам всем нравится XDocument? Он очень медленный и потребляет много памяти - person vitalygolub; 05.11.2015
comment
Это намного лучше, чем XmlDocument? В XDocument меньше инструкций и проще извлекать теги. - person jdweng; 05.11.2015