jjzjj

C# 查找和替换 XML 节点

coder 2024-06-27 原文

编辑:我决定采用推荐的 LINQ to XML 方法(参见下面的答案)并且一切正常,除了我无法用增量记录中的记录替换更改的记录文件。我设法通过删除完整文件节点然后添加增量节点来使程序工作。有没有办法只交换它们?此外,虽然这个解决方案非常好,但有没有什么方法可以在不丢失 LINQ 代码的情况下减少内存使用量?此解决方案可能仍然有效,但我愿意牺牲时间来降低内存使用量。


我正在尝试获取两个 XML 文件(一个完整文件和一个增量文件)并将它们合并在一起。 XML 文件如下所示:

<List>
    <Records>
        <Person id="001" recordaction="add">
            ...
        </Person>
    </Records>
</List>

recordaction 属性也可以是“chg”表示更改或“del”表示删除。我的程序的基本逻辑是:

1) 将完整文件读入 XmlDocument。

2) 将增量文件读入 XmlDocument,使用 XmlDocument.SelectNodes() 选择节点,将这些节点放入字典中以便于搜索。

3) 选择完整文件中的所有节点,遍历并对照包含增量记录的字典检查每个节点。如果 recordaction="chg"或 "del"将节点添加到列表,然后从 XmlNodeList 中删除该列表中的所有节点。最后,将增量文件中的 recordaction="chg"或 "add"记录添加到完整文件中。

4) 保存 XML 文件。

我在执行第 3 步时遇到了一些严重的问题。这是该函数的代码:

private void ProcessChanges(XmlNodeList nodeList, Dictionary<string, XmlNode> dictNodes)
    {
        XmlNode lastNode = null;
        XmlNode currentNode = null;
        List<XmlNode> nodesToBeDeleted = new List<XmlNode>();

        // If node from full file matches to incremental record and is change or delete, 
        // mark full record to be deleted.
        foreach (XmlNode fullNode in fullDocument.SelectNodes("/List/Records/Person"))
        {
            dictNodes.TryGetValue(fullNode.Attributes[0].Value, out currentNode);
            if (currentNode != null)
            {
                if (currentNode.Attributes["recordaction"].Value == "chg"
                    || currentNode.Attributes["recordaction"].Value == "del")
                {
                    nodesToBeDeleted.Add(currentNode);
                }
            }
            lastNode = fullNode;
        }

        // Delete marked records
        for (int i = nodeList.Count - 1; i >= 0; i--)
        {
            if(nodesToBeDeleted.Contains(nodeList[i]))
            {
                nodeList[i].ParentNode.RemoveChild(nodesToBeDeleted[i]);
            }
        }

        // Add in the incremental records to the new full file for records marked add or change.
        foreach (XmlNode weeklyNode in nodeList)
        {
            if (weeklyNode.Attributes["recordaction"].Value == "add"
                || weeklyNode.Attributes["recordaction"].Value == "chg")
            {
                fullDocument.InsertAfter(weeklyNode, lastNode);
                lastNode = weeklyNode;
            }
        }
    }

传入的 XmlNodeList 只是从增量文件中选择的所有增量记录,而字典只是那些相同的节点,但在 id 上键入,所以我不必遍历所有每次增量记录。现在,由于索引越界,该程序正处于“删除标记记录”阶段。我很确定“添加增量记录”也不起作用。有任何想法吗?还有一些关于提高效率的建议会很好。我可能会遇到一个问题,因为它正在读取一个 250MB 的文件,该文件在内存中膨胀到 750MB,所以我想知道是否有更简单的方法在整个文件中逐个节点。谢谢!

最佳答案

下面是一个示例,说明您可以如何使用 LINQ-to-XML 完成它。不需要字典:

using System.Xml.Linq;

// Load the main and incremental xml files into XDocuments
XDocument fullFile = XDocument.Load("fullfilename.xml");
XDocument incrementalFile = XDocument.Load("incrementalfilename.xml");    

// For each Person in the incremental file
foreach (XElement person in incrementalFile.Descendants("Person")) {

    // If the person should be added to the full file
    if (person.Attribute("recordaction").Value == "add") {
        fullFile.Element("List").Element("Records").Add(person); // Add him
    }

    // Else the person already exists in the full file
    else {
        // Find the element of the Person to delete or change
        var personToChange =
                (from p in fullFile.Descendants("Person")
                    where p.Attribute("id").Value == person.Attribute("id").Value
                    select p).Single();

        // Perform the appropriate operation
        switch (person.Attribute("recordaction").Value) {
            case "chg":
                personToChange.ReplaceWith(person);
                break;
            case "del":
                personToChange.Remove();
                break;
            default:
                throw new ApplicationException("Unrecognized attribute");
        }
    }
}// end foreach

// Save the changes to the full file
fullFile.Save("fullfilename.xml");

如果您在运行它时遇到任何问题,请告诉我,我会进行编辑和修复。我很确定它是正确的,但目前没有可用的 VS。

编辑:修复了 "chg" 案例以使用 personToChange.ReplaceWith(person) 而不是 personToChange = person。后者不会替换任何内容,因为它只是将引用从基础文档中移开。

关于C# 查找和替换 XML 节点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6794027/

有关C# 查找和替换 XML 节点的更多相关文章

  1. ruby-on-rails - 如何从 format.xml 中删除 <hash></hash> - 2

    我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为

  2. ruby 正则表达式 - 如何替换字符串中匹配项的第 n 个实例 - 2

    在我的应用程序中,我需要能够找到所有数字子字符串,然后扫描每个子字符串,找到第一个匹配范围(例如5到15之间)的子字符串,并将该实例替换为另一个字符串“X”。我的测试字符串s="1foo100bar10gee1"我的初始模式是1个或多个数字的任何字符串,例如,re=Regexp.new(/\d+/)matches=s.scan(re)给出["1","100","10","1"]如果我想用“X”替换第N个匹配项,并且只替换第N个匹配项,我该怎么做?例如,如果我想替换第三个匹配项“10”(匹配项[2]),我不能只说s[matches[2]]="X"因为它做了两次替换“1fooX0barXg

  3. ruby-on-rails - 在 ruby​​ 中使用 gsub 函数替换单词 - 2

    我正在尝试用ruby​​中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了

  4. ruby - 当使用::指定模块时,为什么 Ruby 不在更高范围内查找类? - 2

    我刚刚被困在这个问题上一段时间了。以这个基地为例:moduleTopclassTestendmoduleFooendend稍后,我可以通过这样做在Foo中定义扩展Test的类:moduleTopmoduleFooclassSomeTest但是,如果我尝试通过使用::指定模块来最小化缩进:moduleTop::FooclassFailure这失败了:NameError:uninitializedconstantTop::Foo::Test这是一个错误,还是仅仅是Ruby解析变量名的方式的逻辑结果? 最佳答案 Isthisabug,or

  5. ruby - 查找字符串中的内容类型(数字、日期、时间、字符串等) - 2

    我正在尝试解析一个CSV文件并使用SQL命令自动为其创建一个表。CSV中的第一行给出了列标题。但我需要推断每个列的类型。Ruby中是否有任何函数可以找到每个字段中内容的类型。例如,CSV行:"12012","Test","1233.22","12:21:22","10/10/2009"应该产生像这样的类型['integer','string','float','time','date']谢谢! 最佳答案 require'time'defto_something(str)if(num=Integer(str)rescueFloat(s

  6. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

  7. C# 到 Ruby sha1 base64 编码 - 2

    我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

  8. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  9. ruby - Ruby gsub 替换中的行为不一致? - 2

    两个gsub产生不同的结果。谁能解释一下为什么?代码也可在https://gist.github.com/franklsf95/6c0f8938f28706b5644d获得.ver=9999str="\tCFBundleDevelopmentRegion\n\ten\n\tCFBundleVersion\n\t0.1.190\n\tAppID\n\t000000000000000"putsstr.gsub/(CFBundleVersion\n\t.*\.).*()/,"#{$1}#{ver}#{$2}"puts'--------'putsstr.gsub/(CFBundleVersio

  10. ruby-on-rails - 在这种情况下我如何模拟一个对象?没有明显的方法可以用模拟替换对象 - 2

    假设我在Store的模型中有这个非常简单的方法:defgeocode_addressloc=Store.geocode(address)self.lat=loc.latself.lng=loc.lngend如果我想编写一些不受地理编码服务影响的测试脚本,这些脚本可能已关闭、有限制或取决于我的互联网连接,我该如何模拟地理编码服务?如果我可以将地理编码对象传递到该方法中,那将很容易,但我不知道在这种情况下该怎么做。谢谢!特里斯坦 最佳答案 使用内置模拟和stub的rspecs,你可以做这样的事情:setupdo@subject=MyCl

随机推荐