【问题标题】:C# Linq-XML find match and replace elementsC# Linq-XML 查找匹配和替换元素
【发布时间】:2014-03-16 08:10:34
【问题描述】:

我正在编写一个将文本转换为 XML 的快速工具。

我创建了一个包含音符编号和新名称的字典。

我想在 XML 中搜索“INote”值(在示例中为“118”)并在 Dictionary 中找到相应的键并替换“Name”值(在示例中为“Sound 119” )

XML 结构如下所示

  <list name="Quantize" type="list">
      <item>
         <int name="Grid" value="4"/>
         <int name="Type" value="0"/>
         <float name="Swing" value="0"/>
         <int name="Unquantized" value="0"/>
         <int name="Legato" value="50"/>
      </item>
   </list>
   <list name="Map" type="list">
      <item>
         <int name="INote" value="118"/>
         <int name="ONote" value="118"/>
         <int name="Channel" value="9"/>
         <float name="Length" value="200"/>
         <int name="Mute" value="0"/>
         <int name="DisplayNote" value="118"/>
         <int name="HeadSymbol" value="0"/>
         <int name="Voice" value="0"/>
         <int name="PortIndex" value="0"/>
         <string name="Name" value="Sound 119" wide="true"/>
         <int name="QuantizeIndex" value="0"/>
      </item>
      <item>
      ....
      </item>
   </list>    
  <list name="Order" type="int">
      <item value="0"/>
      <item value="1"/>
      <item value="2"/>
  </list>

到目前为止,这是我的代码:

   XDocument outFile = XDocument.Load(outputFile);

    var currItem = from item in outFile.Descendants("item")
                 select item;
    foreach (var i in currItem) // this loop is executed many times
    {
        var node = i.Element("int");
        if (node ==null) continue;
        var name = node.Attribute("name").Value;
        var value = i.Element("int").Attribute("value").Value;
        if (name == "INote")
        {
            var str = i.Element("string").Attribute("name").Value;
            if (str == "Name")
            {
                var snd = i.Element("string").Attribute("value").Value;
                MessageBox.Show(string.Format("{0} - {1} - {2} - {3}", name, value,str,snd));
            }
        }
    }

我有两个问题:

  • 查询:有没有办法直接在查询本身而不是在 foreach 循环中执行检查? (作为附加信息,并非所有元素都包含具有“INote”属性的元素)

  • foreach 循环:整个循环重复了 n 次以上,因此我多次获得从 0 到 n 的 Messagebox。

任何帮助将不胜感激。

答案: 谢谢大家,经过一些测试,我想我已经解决了这个问题:

        XDocument outFile = XDocument.Load(outputFile);
        var NamesData = outFile.Root.Elements("list")
            .Where(x => x.Attribute("name").Value == "Map")
            .Elements("item")
            .Select(y =>
                new KeyValuePair<XAttribute,XAttribute>
                (
                       y.Elements("int").Where(c => c.Attribute("name").Value == "INote").Select(c => c.Attribute("value")).FirstOrDefault(),
                       y.Elements("string").Where(c => c.Attribute("name").Value == "Name").Select(c => c.Attribute("value")).FirstOrDefault()
                )
                );

我创建了一个 XAttributes 的 KeyValuePair,这样我就可以轻松地将它们保存回来:

顺便说一句,这是方法的其余部分(注意定义它只是一个数据容器)

        foreach (var data in NamesData)
        {
            NoteDefinition newName;
            if (internalDictionary.TryGetValue(Convert.ToInt32(data.Key.Value), out newName))
            {
                data.Value.SetValue(newName.voiceName);
            }
        }


        outFile.Save(outputFile);

【问题讨论】:

    标签: c# xml linq


    【解决方案1】:

    对于问题 #1:是的。我通常使用 XPath 从 XElements 中选择特定元素。这是您的代码的一个工作示例:

    XDocument outFile = XDocument.Parse(xDec + XmlDocumentAsString);
    
    foreach (var i in outFile.XPathSelectElements("item/list/item")) // this loop is executed many times
    {
        var node = i.Element("int");
        if (node == null) continue;
        var name = node.Attribute("name").Value;
        var value = i.Element("int").Attribute("value").Value;
        if (name == "INote")
        {
            var str = i.Element("string").Attribute("name").Value;
            if (str == "Name")
            {
                var snd = i.Element("string").Attribute("value").Value;
                MessageBox.Show(string.Format("{0} - {1} - {2} - {3}", name, value, str, snd));
            }
        }
    }
    

    这是我用来刷新 XPath 语法的快速备忘单:http://www.w3schools.com/xpath/xpath_syntax.asp

    对于问题 #2: 你可以使用'break;'停止迭代,例如“foreach”。方法如下:

    MessageBox.Show(string.Format("{0} - {1} - {2} - {3}", name, value, str, snd));
    break;
    

    编辑:这是一个更高级的 XPath,所以我不想将它添加到第一个示例中。但是由于您正在检查元素是否有一个名为“int”的子元素,而该子元素又具有一个名为“named”的属性,其值为“INote”,这是您想要的唯一注释,但您需要它的父母值,然后您可以将其添加到 XPath 并删除检查:

    foreach (var i in outFile.XPathSelectElements("item/list/item/int[@name='INote']/.."))
    {
        var name = i.Element("int").Attribute("name").Value;
        var value = i.Element("int").Attribute("value").Value;
    
        var str = i.Element("string").Attribute("name").Value;
        if (str == "Name")
        {
            var snd = i.Element("string").Attribute("value").Value;
            MessageBox.Show(string.Format("{0} - {1} - {2} - {3}", name, value, str, snd));
            break;
        }
    }
    

    请注意,我添加了“break;”在“MessageBox.Show();”之后线。 foreach 仅针对具有该 int[@name='INote'] 元素的每个父级迭代 1 次,但如果您仍然只需要 1 条消息,则保留“break;”。 :)

    Edit2:我阅读了另一个答案并意识到您可能需要 1 条消息中的所有数据。这是另一种方法:

    var sb = new StringBuilder();
    foreach (var i in outFile.XPathSelectElements("item/list/item/int[@name='INote']/.."))
    {
        var name = i.Element("int").Attribute("name").Value;
        var value = i.Element("int").Attribute("value").Value;
    
        var str = i.Element("string").Attribute("name").Value;
        if (str == "Name")
        {
            var snd = i.Element("string").Attribute("value").Value;
            sb.AppendFormat("{0} - {1} - {2} - {3}", name, value, str, snd);
        }
    }
    MessageBox.Show(sb.ToString().TrimEnd());
    

    【讨论】:

    • 非常感谢!是的,问题不在于中断,而在于我在某些集合上运行 foreach 并且我再次将其重新枚举
    • 再次感谢,第二个解决方案似乎也很有趣。对于消息框,它只是一个测试,看看循环是否有效,显然有什么问题。反正问题现在解决了。
    【解决方案2】:

    你可以这样做:

    var qry = from item in outFile.Descendants("item")
                let nodeInt = item.Element("int")
                let nodeStr = item.Element("string")
                where nodeInt != null && nodeStr != null
                let name = nodeInt.Attribute("name").Value
                let value = nodeInt.Attribute("value").Value
                let str = nodeStr.Attribute("name").Value
                let snd = nodeStr.Attribute("value").Value
                where name == "INote" && str == "Name"
                select string.Format("{0} - {1} - {2} - {3}", name, value, str, snd);
    MessageBox.Show(string.Join(Environment.NewLine, qry));
    

    【讨论】:

    • 谢谢,这看起来很酷,我已经编辑了答案以展示我最后的做法。
    猜你喜欢
    • 1970-01-01
    • 2020-01-18
    • 2012-06-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-12
    相关资源
    最近更新 更多