我正在编写一个快速工具,可以将文本转换为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);
答案 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));