2017-01-12 2 views
2

Я изучаю F # и столкнулся с вопросом, для которого поисковая система не очень помогла мне.Выбор объекта из объекта с использованием последовательностей в F #

У меня есть документ Xml с XmlNodes, которые выбираются с помощью Xpath. Я отфильтровал атрибуты, и коллекция атрибутов может быть возвращена из Seq. Однако, когда я возвращаю значение атрибута вместо атрибута, следующее сообщение об ошибке отображается при компиляции

This expression was expected to have type 'a option
but here has type string

Фрагмент кода приведен ниже

let doc = new System.Xml.XmlDocument() in 
        doc.LoadXml xml; 
        doc.SelectNodes "//*[local-name()='SingleSignOnService']" 
         |> Seq.cast<System.Xml.XmlNode> 
         |> Seq.collect (fun node -> node.Attributes |> Seq.cast<System.Xml.XmlAttribute>) 
         |> Seq.filter (fun attr -> attr.Name.Equals("Binding",StringComparison.OrdinalIgnoreCase)) 
         |> Seq.choose(fun attr -> attr.Value) 

Пожалуйста, предложите мне правильный подход.

EDIT

Вот решение, которое я создал с помощью Mr.Marklam. Надеюсь, это поможет кому-либо еще

let doc = new System.Xml.XmlDocument() in 
        doc.LoadXml xml; 
        doc.SelectNodes "//*[local-name()='SingleSignOnService']" 
         |> Seq.cast<System.Xml.XmlNode> 
         |> Seq.collect (fun node -> node.Attributes |> Seq.cast<System.Xml.XmlAttribute> |> Seq.filter (fun attr -> attr.Name.Equals("Binding",StringComparison.OrdinalIgnoreCase))) 
         |> Seq.choose (fun attr -> if (attr.Name.Equals("Binding",StringComparison.OrdinalIgnoreCase)) then Some attr.Value else None) 
+2

Вы пробовали 'Seq.map' вместо' Seq.choose'? – s952163

ответ

4

Если Вы желаете использовать Seq.choose, чтобы удалить любые null значения атрибутов, вы должны преобразовать attr.Value в string option.

Самый простой способ сделать это состоит в трубу через Option.ofObj, т.е.

|> Seq.filter (fun attr -> attr.Name.Equals("Binding",StringComparison.OrdinalIgnoreCase) 
|> Seq.choose(fun attr -> attr.Value |> Option.ofObj) 

Но если вы знаете, что значение никогда не нулевой, то вы могли бы использовать

|> Seq.choose (fun attr -> if attr.Name.Equals("Binding",StringComparison.OrdinalIgnoreCase)) then Some attr.Value else None) 
+0

Большое спасибо за вашу помощь. Одна небольшая помощь - это опция «Option.ofObj», доступная в F # для Visual Studio 2013, поскольку я не могу ее найти. Кроме того, добавьте '(' после ключевого слова if в Seq.choose, поскольку у него нет соответствующей скобки для начала. Также попросите хорошую книгу, чтобы я хорошо изучил F #. – Saravanan

+0

'' Option.ofObj'' находится в F # 4, который, похоже, не доступен в 2013 году. Вы можете определить свой собственный как '' let ofObj x = if x = null then None else Some x '' – marklam

3

Я собираю есть некоторая путаница с методами расширения Linq.

map(fun attr -> attr.Value) является Select(attr => attr.Value)

choose просто Where(option => option.HasValue).Select(option => option.Value)

Here're несколько более эквивалентов в C#/Linq

collect является SelectMany

find является First

fold является Aggregate

singleton является Enumerable.Return

+2

'select' не эквивалентен' Where' . LINQ эквивалентно 'select', поскольку обычно он не использует типы опций. В F #' filter' эквивалентно 'Where'.'select' можно рассматривать как объединенный' map' и 'filter', где значения' None' отфильтровываются, а 'Some x' становится' f x'. – TheInnerLight

+0

@ TheInnerLight Хороший улов. Ред. – Asti

+0

Благодарим вас за сравнение C# и его эквивалентов F #. Я буду иметь это в своих заметках F #. Как упоминалось в предыдущем ответе, пожалуйста, поделитесь хорошей книгой F # для моего обучения. – Saravanan