Сканирование XML с C# на F#
Пытаясь изучить F#, и я попытался переопределить следующую функцию в F#
private string[] GetSynonyms(string synonyms)
{
var items = Enumerable.Repeat(synonyms, 1)
.Where(s => s != null)
.Select(XDocument.Parse)
.Select(doc => doc.Root)
.Where(root => root != null)
.SelectMany(e => e.Elements(SynonymsNamespace + "synonym"))
.Select(e => e.Value)
.ToArray();
return items;
}
Я получил это далеко сам
let xname = XNamespace.Get "http://localuri"
let syn = "<synonyms xmlns=\"http://localuri\"><synonym>a word</synonym><synonym>another word</synonym></synonyms>"
let synonyms str =
let items = [str]
items
|> List.map System.Xml.Linq.XDocument.Parse
|> List.map (fun x -> x.Root)
|> List.map (fun x -> x.Elements(xname + "synonym") |> Seq.cast<System.Xml.Linq.XElement>)
|> Seq.collect (fun x -> x)
|> Seq.map (fun x -> x.Value)
let a = synonyms syn
Dump a
Теперь мне интересно, есть ли более функциональный способ написания того же кода.
Извлекая доступ к свойствам автономных функций, я получил эту версию
let xname = XNamespace.Get "http://localuri"
let syn = "<synonyms xmlns=\"http://localuri\"><synonym>a word</synonym><synonym>another word</synonym></synonyms>"
let getRoot (doc:System.Xml.Linq.XDocument) = doc.Root
let getValue (element:System.Xml.Linq.XElement) = element.Value
let getElements (element:System.Xml.Linq.XElement) =
element.Elements(xname + "synonym")
|> Seq.cast<System.Xml.Linq.XElement>
let synonyms str =
let items = [str]
items
|> List.map System.Xml.Linq.XDocument.Parse
|> List.map getRoot
|> List.map getElements
|> Seq.collect (fun x -> x)
|> Seq.map getValue
let a = synonyms syn
Dump a
Но у меня все еще есть некоторые проблемы
- Могу ли я переписать этот Seq.collect (fun x -> x) по-другому? Это звучит излишне
- Могу ли я удалить все это (весело x -> x.Property) без создания новых функций?
- Как на самом деле вернуть массив, а не Seq<'a> (я так понимаю, это IEnumerable<' a>)
Спасибо
2 ответа
Seq.collect (fun x -> x) можно переписать с помощью предопределенной функции id в идентификатор Seq.collect.
В F# 4.0 его можно удалить только для конструкторов.
использовать Seq.toArray или Seq.toList
Было бы очень неправильно отбросить C#-код и пойти ва-банк с провайдером XML в F#? В моем мире всегда неправильно анализировать XML, когда существуют другие решения (если только я не пытаюсь создать восьмиугольные колеса или влажные порохы, которые другие сделали лучше до меня).
В этом отношении я бы даже использовал какое-то преобразование (XSLT) или выделение (XPATH/XQUERY), если бы я не мог использовать XML-провайдера или какой-либо XSD (C#) для генерации кода.
Если по какой-то причине XML так неструктурирован, что вам действительно нужен синтаксический анализ, то, возможно, XML ошибочен...
При использовании XmlProvider вы получаете пространство имен, типы и т. Д. Бесплатно...
#r @"..\correct\this\path\to\packages\FSharp.Data.2.2.5\lib\net40\FSharp.Data.dll"
#r "System.Xml.Linq"
open FSharp.Data
[<Literal>]
let syn = "<synonyms xmlns=\"http://localuri\"><synonym>a word</synonym><synonym>another word</synonym></synonyms>"
type Synonyms = XmlProvider<syn>
let a = Synonyms.GetSample()
a.Synonyms |> Seq.iter (printfn "%A")
Помните, что XmlProvider также может принимать файлы или URL-адреса в качестве примеров для вывода типов и т. Д., И что вы также можете использовать этот код в качестве примера и затем использовать
let a = Synonyms.Load(stuff)
где вещи - это чтение из потока, чтения текста или URI и вывод в соответствии с вашим примером. Образец и материал могут даже указывать на один и тот же файл /Uri, если это стандартное размещение данных.
Смотрите также: http://fsharp.github.io/FSharp.Data/library/XmlProvider.html