我正在编写一个从文本到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));