Можно ли изменить значение узла с помощью xmlreader? - PullRequest
0 голосов

Я читаю в потоке XML размером приблизительно 100 МБ, и я хотел бы заменить значения, которые превышают 1 МБ.

пример ввода

<root>
    <visit>yes</visit>
    <filedata>SDFSFDSDFfgdfgsgdf==(this is 5 mb)</filedata>
    <type>pdf</type>
    <moredata>sssssssssssssss (this 2mb)</moredata>
</root>

ожидаемый вывод

<root>
    <visit>yes</visit>
    <filedata>REPLACED TEXT</filedata>
    <type>pdf</type>
    <moredata>REPLACED TEXT</moredata>
</root>

Вот что я использую для чтения потока, а также проверки размера:

XmlReader rdr = XmlReader.Create (new System.IO.StringReader (xml));
while (rdr.Read ()) {
    if (rdr?.Value.Length > ONEMEGABYTE) {
        //replace value with "REPLACE TEXT"}
    }

Как заменить значение в rdr.Value?

Ответы [ 3 ]

1 голос
/ 22 мая 2019

Вы можете создать подкласс XmlReader, чтобы "отфильтровать" нежелательные элементы, а затем использовать XmlDocument.Load() со своим считывателем вместо того, чтобы позволять ему создавать свои собственные.

Обратите внимание, что это исключит только значение ошибочных тегов: если вы установите точку останова в цикле Read (), вы обнаружите, что <foo>bar</foo> состоит из трех частей: <foo> имеет Элемент NodeType без значения, "bar" имеет Текст NodeType с пустым LocalName, а </foo> - это EndElement NodeType без значения. Если бы «бар» превышал предельную длину, приведенный ниже «фильтр» превратил бы <foo>bar</foo> в <foo></foo> Чтобы исключить все значения <foo>bar</foo>, основанные на длине «бара», вам придется смотреть в будущее. Выполнимо, но, возможно, не стоит вашего времени. Надеюсь, это не является обязательным требованием.

Альтернативой (или дополнением) этому классу может быть версия этого с Func<string, string>, через которую проходит каждый Value: s => (s.Length > MAX_LEN) ? "" : s.

Кроме того, насколько я знаю, XmlTextReaderImpl (фактический тип _reader) может кэшировать весь текст и в любом случае убить вашу производительность. Возможно, вам придется написать свои собственные смелости для этой вещи.

public class FilteredXmlReader : XmlReader
{
    public Func<XmlReader, bool> Filter;

    private XmlReader _reader;
    private FilteredXmlReader(TextReader input, Func<XmlReader, bool> filterProc)
    {
        Filter = filterProc;
        _reader = XmlReader.Create(input);
    }

    public static new XmlReader Create(TextReader input, Func<XmlReader, bool> filterProc)
    {
        return new FilteredXmlReader(input, filterProc);
    }

    public override bool Read()
    {
        var b = _reader.Read();

        while (!(bool)Filter?.Invoke(_reader))
        {
            b = _reader.Read();
        }

        return b;
    }

    #region Wrapper Boilerplate

    public override XmlNodeType NodeType => _reader.NodeType;

    public override string LocalName => _reader.LocalName;

    public override string NamespaceURI => _reader.NamespaceURI;

    public override string Prefix => _reader.Prefix;

    public override string Value => _reader.Value;

    public override int Depth => _reader.Depth;

    public override string BaseURI => _reader.BaseURI;

    public override bool IsEmptyElement => _reader.IsEmptyElement;

    public override int AttributeCount => _reader.AttributeCount;

    public override bool EOF => _reader.EOF;

    public override ReadState ReadState => _reader.ReadState;

    public override XmlNameTable NameTable => _reader.NameTable;

    public override string GetAttribute(string name) => _reader.GetAttribute(name);

    public override string GetAttribute(string name, string namespaceURI) => _reader.GetAttribute(name, namespaceURI);

    public override string GetAttribute(int i) => _reader.GetAttribute(i);

    public override string LookupNamespace(string prefix) => _reader.LookupNamespace(prefix);

    public override bool MoveToAttribute(string name) => _reader.MoveToAttribute(name);

    public override bool MoveToAttribute(string name, string ns) => _reader.MoveToAttribute(name, ns);

    public override bool MoveToElement() => _reader.MoveToElement();

    public override bool MoveToFirstAttribute() => _reader.MoveToFirstAttribute();

    public override bool MoveToNextAttribute() => _reader.MoveToNextAttribute();

    public override bool ReadAttributeValue() => _reader.ReadAttributeValue();

    public override void ResolveEntity() => _reader.ResolveEntity();

    #endregion Wrapper Boilerplate
}

Использование:

var xml = "<test />";
XmlDocument doc = new XmlDocument();

XmlReader rdr = FilteredXmlReader.Create(new System.IO.StringReader(xml), 
                    r => r?.Value.Length < 20);

var filteredXML = doc.OuterXml;
0 голосов
/ 22 мая 2019

Мы можем достичь этого с помощью XmlDocument.Получение всех дочерних узлов корневого узла и последующий цикл по всем узлам -

        XmlDocument Doc = new XmlDocument();
        Doc.Load(@"yourpath.xml");
        XmlNodeList xmlNodelist = Doc.DocumentElement.ChildNodes;
        foreach (XmlNode node in xmlNodelist)
        {
            if(node.InnerText.Length > ONEMEGABYTE)
            {
                node.InnerText = "new value";
            }
        }
        Doc.Save(@"yourpath.xml"); //will replace new changes in the source file.
0 голосов
/ 22 мая 2019

Вот пример замены с использованием Xml Reader и Xml Linq

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


namespace ConsoleApplication29
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {
            XmlReader reader = XmlReader.Create(FILENAME);

            while (!reader.EOF)
            {
                if (reader.Name != "visits")
                {
                    reader.ReadToFollowing("visits");
                }
                if (!reader.EOF)
                {
                    XElement visits = (XElement)XElement.ReadFrom(reader);
                    XElement filedata = visits.Element("filedata");
                    filedata.SetValue("New Data");

                }
            }

        }
    }
}

Вот xml, который я использовал

<root>
  <visits>
    <visit>yes</visit>
    <filedata>REPLACED TEXT</filedata>
    <type>pdf</type>
    <moredata>REPLACED TEXT</moredata>
  </visits>
</root>
...