c# Linq-XML查找匹配和替换元素



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

我已经创建了一个Dictionary,其中包含注释号和新名称。

我想在XML中搜索"INote"值(在示例中为"118"),并在字典中找到相应的键,并替换"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,所以我可以很容易地保存它们:

这是方法的剩余部分(Notedefinition,它只是一个数据容器)

        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);

问题#1:是的。我通常使用XPath从xelement中选择特定的元素。下面是您的代码的工作示例:

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;
    }
}

注意,我在"MessageBox.Show();"行后面添加了'break;'。foreach只对每个具有int[@name='INote']元素的父元素迭代1次,但如果您仍然只想要1条消息,那么保留'break;'。:)

Edit2:我读了另一个答案,意识到您可能希望在一条消息中包含所有数据。下面是另一种方法:

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());

你可以这样做:

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));

相关内容

  • 没有找到相关文章

最新更新