2012-04-05 2 views
2

Недавно я столкнулся с ситуацией, когда мне пришлось использовать XPath как часть запроса Linq to XML и обнаружил, что он действует немного странно. Я уверен, что есть другие способы решения проблемы с использованием собственных методов XElement API, но в этой ситуации XPath был намного лучше подходит для определения конкретных текстовых значений XML, которые мне нужны, поскольку структура документа была довольно сложной. В конце концов я нашел способ заставить его работать (см. Ниже), но это было не очень красиво, что обычно означает, что я что-то пропустил.Использование XPathEvaluate для получения значения текстового узла в запросе Linq-to-Xml

Мой вопрос в том, как вы можете использовать XPathEvalute или какой-либо другой метод XPath для возврата строкового значения, которое может использоваться в инструкции where или как часть нового объекта в методе select. Я нашел решение, которое использует много литья и Null coalesing operator in LINQ, но должен быть более чистый способ. Корень проблемы заключается в том, что метод XPathEvaluate обычно возвращает элемент XText (отлитый от общего объекта), но Linq преобразует его в IEnumerable, чтобы он мог задержать выполнение. Это вызывает проблемы при попытке получить доступ к значению этого объекта, так как оно не будет реализовано до тех пор, пока запрос не будет выполнен.

Вот пример: Используя следующий документ XML, найдите всех родителей, у которых есть мальчик и девочка с тем же именем. Это ДОЛЖНО выполняться с использованием запроса XPath, так как в моей ситуации структура документа была очень сложной.

//Use C# Program Language type in LinqPad 
void Main() 
{ 
    var xml = XElement.Parse(@"<Root> 
     <Parent> 
      <Boy>Anne</Boy> 
     </Parent> 
     <Parent> 
      <Boy>Alex</Boy> 
      <Girl>Alex</Girl> 
     </Parent> 
     <Parent> 
      <Boy>Jay</Boy> 
     </Parent> 
    </Root>"); 

    var q1 = from e in xml.XPathSelectElements("/Parent") 
      select e; 

    q1.Dump(); 

    var q2 = from e in q1 
      let boy = ((IEnumerable<Object>)e.XPathEvaluate("Boy/text()")).Cast<System.Xml.Linq.XText>().FirstOrDefault() ?? new System.Xml.Linq.XText("") 
      let girl = ((IEnumerable<Object>)e.XPathEvaluate("Girl/text()")).Cast<System.Xml.Linq.XText>().FirstOrDefault() ?? new System.Xml.Linq.XText("") 
      select new {t=boy.GetType(), b=boy, g=girl, same=(boy.Value==girl.Value)}; 

    q2.Dump(); 
} 

Результаты:

enter image description here

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

+0

Вы могли бы упростить XPath, чтобы получить все родители с критериями, которые вы ищете: // Родитель [Boy/текст() = девушка/текст()] – JWiley

ответ

5

Допустимо использовать XPathSelectElement вместо XPathEvaluate?

var q2 = from e in q1 
     let boy = (string)e.XPathSelectElement("Boy") 
     let girl = (string)e.XPathSelectElement("Girl") 
     where boy.StartsWith("A") //Optional use in where statement 
     select new { t = boy.GetType(), b = boy, g = girl, same = (boy == girl) }; 
+0

Это лучше, чем у меня :(И это – Phil

+2

Операторы литья 'XElement' очень удобны. –

+0

Очень хорошо! Я пробовал это, но мне нужен текстовый узел, а не полный элемент. Похоже, все, что мне нужно, - это отличить строку THANKS! –

0

Как насчет этого, не уверен, что вам нужно «t».

var q2 = q1.Select (q => 
    new 
    { 
     Boy = q.XPathSelectElement("Boy"), 
     Girl = q.XPathSelectElement("Girl"), 
    } 
); 

var q3 = q2.Select (q => 
    new 
    { 
     Boy = q.Boy == null ? string.Empty : q.Boy.Value, 
     Girl = q.Girl == null ? string.Empty : q.Girl.Value 
    } 
); 

var q4 = q3.Select (q => new { q.Boy, q.Girl, same=q.Boy==q.Girl }); 

q4.Dump(); 

Что можно переписать в виде

var q2 = q1. 
    Select (q => new { Boy = q.XPathSelectElement("Boy"),Girl = q.XPathSelectElement("Girl"),}). 
    Select (q => new { Boy = q.Boy == null ? string.Empty : q.Boy.Value, Girl = q.Girl == null ? string.Empty : q.Girl.Value}). 
    Select (q => new { q.Boy, q.Girl, same=q.Boy==q.Girl } 
); 

q2.Dump(); 
Смежные вопросы