Надежный запрос LINQ to XML для родственных пар ключ-значение
(Первый пост, пожалуйста, будьте нежны!)
Я просто изучаю LINQ to XML во всей его красе и слабости, пытаясь взломать его, чтобы сделать то, что я хочу сделать:
Учитывая XML-файл, как это -
<list>
<!-- random data, keys, values, etc.-->
<key>FIRST_WANTED_KEY</key>
<value>FIRST_WANTED_VALUE</value>
<key>SECOND_WANTED_KEY</key>
<value>SECOND_WANTED_VALUE</value> <!-- wanted because it's first -->
<key>SECOND_WANTED_KEY</key>
<value>UNWANTED_VALUE</value> <!-- not wanted because it's second -->
<!-- nonexistent <key>THIRD_WANTED_KEY</key> -->
<!-- nonexistent <value>THIRD_WANTED_VALUE</value> -->
<!-- more stuff-->
</list>
Я хочу извлечь значения набора известных "требуемых ключей" надежным способом, т.е. если SECOND_WANTED_KEY
появляется дважды, я хочу только SECOND_WANTED_VALUE
не UNWANTED_VALUE
, Дополнительно, THIRD_WANTED_KEY
может появиться или не появиться, поэтому запрос должен быть в состоянии справиться и с этим. Я могу предположить, что FIRST_WANTED_KEY
появится перед другими ключами, но не может предположить ничего о порядке других ключей - если ключ появляется дважды, его значения не важны, мне нужен только первый. Анонимный тип данных, состоящий из строк, это хорошо.
Моя попытка была сосредоточена вокруг чего-то такого:
var z = from y in x.Descendants()
where y.Value == "FIRST_WANTED_KEY"
select new
{
first_wanted_value = ((XElement)y.NextNode).Value,
//...
}
Мой вопрос, что это должно ...
быть? Я пытался, например, (некрасиво, я знаю)
second_wanted_value = ((XElement)y.ElementsAfterSelf()
.Where(w => w.Value=="SECOND_WANTED_KEY")
.FirstOrDefault().NextNode).Value
который, мы надеемся, должен позволить ключу быть где угодно или не существовать, но это не сработало, так как .NextNode
на нуле XElement
не похоже на работу.
Я также пытался добавить в
.Select(t => {
if (t==null)
return new XElement("SECOND_WANTED_KEY","");
else return t;
})
пункт после где, но это тоже не сработало.
Я открыт для предложений, (конструктивной) критики, ссылок, ссылок или предложений фраз в Google и т. Д. Я сделал большую долю в поиске Google и проверке SO, поэтому любая помощь будет принята.
Спасибо!
РЕДАКТИРОВАТЬ: Позвольте мне добавить слой сложности к этому - я должен был включить это в первую очередь. Допустим, документ XML выглядит так:
<lists>
<list>
<!-- as above -->
</list>
<list>
<!-- as above -->
</list>
</lists>
и я хочу извлечь несколько наборов этих пар ключ-значение. Вопрос / Внимание: если SECOND_WANTED_KEY
не появляется в первом <list>
элемент, но появляется во втором, я не хочу случайно забрать второй элемент списка SECOND_WANTED_KEY
,
РЕДАКТИРОВАТЬ № 2:
Как еще одна идея, я попытался создать HashSet
из ключей, которые я ищу и делаю это:
HashSet<string> wantedKeys = new HashSet<string>();
wantedKeys.Add("FIRST_WANTED_KEY");
//...add more keys here
var kvp = from a in x.Descendants().Where(a => wantedKeys.Contains(a.Value))
select new KeyValuePair<string,string>(a.value,
((XElement)a.NextNode).Value);
Это дает мне все пары ключ-значение, но я не уверен, гарантирует ли это, что я правильно "свяжу" пары с их родительским элементом `'. Любые мысли или сравнения между этими двумя подходами будут полезны.
Обновление статуса 4/9/10
На данный момент я все еще думаю, что подход с использованием хеш-набора является наиболее предпочтительным. Кажется, что большая часть обработки XML, выполняемая.NET, выполняется в порядке документов - до сих пор все мои тестовые примеры работали.
Я бы предложил щедрость и / или откликнувшийся ответ, но мне не хватило бы очков за это. Я приму решение сегодня, так что давай! Благодарю.
2 ответа
Это получает значение первого <value>
элемент после первого <key>
элемент, содержащий "SECOND_WANTED_KEY"
:
XDocument doc;
string result = (string)doc.Root
.Elements("key")
.First(node => (string)node == "SECOND_WANTED_KEY")
.ElementsAfterSelf("value")
.First();
Добавьте нулевые проверки по желанию.
XDocument doc = ...
var wantedKeyValuePairs =
from keyElement in doc.Root.Elements("key")
let valueElement = keyElement.ElementsAfterSelf("value").First()
select new { Key = keyElement.Value, Value = valueElement.Value } into kvp
group kvp by kvp.Key into g
select g.First();
Пояснение: этот запрос принимает каждый <key>
элемент и его следование <value>
элемент, и создает пару ключ-значение с этими элементами. Затем он группирует пары ключ-значение по ключу и принимает только первую пару ключ-значение для каждого ключа.