2016-09-25 2 views
1

У меня есть XML-файл (Это файл ITunes) со следующим содержанием:C# XML Выберите словарь узла по ключевому значению

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 
<plist version="1.0"> 
    <dict> 
     <key>Major Version</key><integer>1</integer> 
     <key>Minor Version</key><integer>1</integer> 
     <key>Application Version</key><string>12.3.3.17</string> 

    ... 

     <key>Music Folder</key><string>file://localhost/C:/Users/username/longpath/</string> 
    </dict> 
</plist> 

Как я могу получить значение последней строки, где <key> является " Музыкальная папка '?

Это действительно большой файл, поэтому я бы предпочел не перебирать все узлы.

Я пробовал некоторые из следующих действий:

XmlNode location = xmlDocument.SelectSingleNode("descendant::dict[key='Music Folder']"); 
location = xmlDocument.SelectSingleNode("//text()[.='Music Folder']"); 
location = xmlDocument.SelectSingleNode("string[last()]"); 
location = xmlDocument.SelectSingleNode("dict::string[last()]"); 
location = xmlDocument.SelectSingleNode("descendant::dict[string[last()]]"); 

Я не знаком с XPath выражений и возникают проблемы, делая правильное выражение.

Я также открыт для решений LINQ.

+0

Если папка Музыка всегда последняя, ​​будет '/ plist/dict // key [last()]/text()' работать? – pinkfloydx33

ответ

2

Выражение XPath для документа оценивается в контексте корня документа, так что вы должны указать plist узел идеально, и я предполагаю, что вы хотите строковое значение, так что вам нужно будет перейти к этому узлу.

XmlNode location = xmlDocument.SelectSingleNode("plist/dict/key[.='Music Folder']/following-sibling::string[1]") 

Обратите внимание, что значение не вложено внутри ключевого элемента, но является следующим братом. Учитывая, что это, кажется, в конце концов, вы, вероятно, может сойти с рук:

XmlNode location = xmlDocument.SelectSingleNode("plist/dict/string[last()]") 

Я бы явно указать plist узел в XPath, как он держит возможные матчи к минимуму. Еще один момент, который следует отметить, заключается в том, что ваш XPath, скорее всего, окажется в основе технологии, повторяющей каждый узел в любом случае в зависимости от того, как вы структурируете свое выражение, поскольку некоторые реализации могут применять оптимизацию.

Вы были близки, но я вижу, что вы смешивали операторов и использовали :: или [], где было бы более подходящим /. :: отделяет ось от имени узла, поэтому descendants::dict получает все потомки контекстного узла (xmlDocument) с именем dict. Оператор / является эффективным сокращением для child::. [] - ваш предикат фильтра, который сужает набор результатов.

+0

На самом деле оба варианта работают. Мне нравится первый, немного больше, так как немного более понятно, что это за намерение. Благодаря! – Jahmic

+1

'//string[preceding::key[.="Music Folder"]]/text() 'также будет работать – pinkfloydx33

+0

@Jahmic вы можете использовать следующий-sibling :: * вместо строки, если вы хотите выбрать строку или целочисленный узел. Я согласен с тем, что цель более ясная и более надежная. – strongbutgood

2

Взял 17 миллисекунд, чтобы получить значение последнего узла в документе. Чтобы проверить скорость, я создал документ, содержащий узел Music Folder, как последний узел key, а первые 3 узла, перечисленные в вашем примере, около 11000 раз.

string ReadValue(XDocument xDoc, string key) 
{ 
    var node = xDoc.Element("plist") 
     .Element("dict") 
     .Elements("key") 
     .Single(e => e.Value == key) 
     .NextNode as XElement; 
    return node.Value; 
} 
+0

Это тоже очень хорошо. Я могу использовать это в ситуациях, когда я повторяю ключи. – Jahmic

Смежные вопросы