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

时间:2014-03-15 21:41:38

标签: c# xml linq

我正在编写一个快速工具,可以将文本转换为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次,所以我多次将Messagebox从0到n。

任何帮助都将不胜感激。

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

        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,所以我可以轻松地将它们保存起来:

这是btw方法的其余部分(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);

2 个答案:

答案 0 :(得分: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 synax的快速备忘单:http://www.w3schools.com/xpath/xpath_syntax.asp

对于问题#2: 你可以使用'休息';停止迭代,例如'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();”之后线。 对于具有int [@ name ='INote']元素的每个父级,foreach只迭代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());

答案 1 :(得分:0)

你可以这样做:

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