【问题标题】:is it possible to alter the node value with xmlreader?是否可以使用 xmlreader 更改节点值?
【发布时间】:2019-10-09 03:14:47
【问题描述】:

我正在读取大约 100mb 的 XML 流,我想替换 超过 1mb 的值。

示例输入

<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中的值?

【问题讨论】:

  • 你没有。 XmlReader 读取,顾名思义。您可以编写一个包装阅读器来截断其他消费者的结果,使用XElement(或XmlDocument,如果您确实必须)或在您的处理步骤中注入一些其他逻辑,但不能在读取的循环中。
  • 只需解析XML(XElement或XmlDocument),找到你想要的节点,设置Value
  • 我通常一次读取一段xml,然后解析成一个XElement。请参阅:stackoverflow.com/questions/40944048/…
  • @jdweng 谢谢!但我没有看到这实际上如何改变节点值
  • 获得 XElement(s) 后,您可以使用 Set Value 方法进行更改。

标签: c# .net xml xmldocument xmlreader


【解决方案1】:

您可以将XmlReader 子类化以“过滤”掉不需要的元素,然后将XmlDocument.Load() 与您的阅读器一起使用,而不是让它自己创建。

请注意,这将仅排除有问题的标签的 :如果您在 Read() 循环中放置断点,您会发现 &lt;foo&gt;bar&lt;/foo&gt; 分为三个部分:@987654324 @ 有没有值的 NodeType 元素,“bar”有 NodeType Text,有一个空的 LocalName,&lt;/foo&gt; 是没有值的 NodeType EndElement。如果“bar”超过限制长度,下面的“过滤器”会将&lt;foo&gt;bar&lt;/foo&gt; 变成&lt;foo&gt;&lt;/foo&gt; 要根据“bar”的长度排除所有&lt;foo&gt;bar&lt;/foo&gt;,您必须向前看。可行,但可能不值得你花时间。希望这不是这里的要求。

这个类的替代(或添加)可能是一个带有Func&lt;string, string&gt; 的版本,每个Value 都通过:s =&gt; (s.Length &gt; 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;

【讨论】:

    【解决方案2】:

    这里是使用 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>
    

    【讨论】:

    • 更改保留在哪里?你真的在修改文件吗?
    • 我需要同时拥有原始和结果的有效载荷,我正在从流中读取,但不了解在我们执行 SetValue 时实际发生了什么变化@
    • 另外,请记住,我只想修改大于 1mb 的节点,我不明白您的示例如何允许我这样做,因为它假设我知道节点名称,即filedata
    【解决方案3】:

    我们可以通过使用 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.
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-17
      • 2021-12-11
      • 1970-01-01
      • 1970-01-01
      • 2022-01-23
      • 1970-01-01
      相关资源
      最近更新 更多