F# XML parsing

15,617

Solution 1

let doc = new XmlDocument() in
    doc.LoadXml xml;
    doc.SelectNodes "/EmailList/Email/text()"
        |> Seq.cast<XmlNode>
        |> Seq.map (fun node -> node.Value)
        |> String.concat Environment.NewLine

If you actually want the final trailing newline you can add it in the map and String.concat with the empty string.

Solution 2

The same with the F# Data XML Type Provider:

type EmailList = XmlProvider<"""<EmailList><Email>email</Email><Email>email</Email></EmailList>""">
let data = EmailList.Parse("""
    <EmailList>
        <Email>[email protected]</Email>
        <Email>[email protected]</Email>
    </EmailList>
    """)

let emailList = data.Emails |> String.concat Environment.NewLine

Solution 3

Here is similar code to do this in F#. I'm sure one of the F# ninja's will put a better version up here in a minute.

open System.Xml

let getList x = 
    let getDoc =
        let doc = new XmlDocument()
        doc.LoadXml(x) |> ignore
        doc
    let getEmail (n:XmlNode) = n.InnerText.ToString() 
    let doc = getDoc
    let build = new System.Text.StringBuilder()
    doc.SelectNodes("//EmailList") 
        |> Seq.cast<XmlNode>
        |> Seq.map (fun n -> n.ChildNodes )
        |> Seq.map_concat (Seq.cast<XmlNode>)
        |> Seq.map(fun (n:XmlNode) -> getEmail n) 
        |> Seq.iter (fun e -> build.AppendLine(e) |> ignore )
    build.ToString()

Solution 4

If you look at your code, you have a couple of things going on. The first is loading the collection for the Email nodes, and the second is actually doing something meaningful with them.

First, you'd want to have your function return a collection. Something like (and I'm on my Mac, so this may not work):


List<string> EmailAddresses(string xml)
{
    XmlDocument xdoc = new XmlDocument();
    XmlNodeList nodeList;
    String emailList = string.Empty;
    xdoc.LoadXml(xml);
    nodeList = xdoc.SelectNodes("//EmailList");
    foreach (XmlNode item in nodeList)
    {
        foreach (XmlNode email in item)
        {
             yield email.InnerText.ToString();
        }               
    }
}

Now the question comes around what you want to do with that collection. In your example above, you were concatenating it, or basically storing state.

From a C# perspective, you can then start using things like the answers from this question (using LINQ) and this one.

From an F# perspective, you now have a list and can simply use the normal procedures for a list like this one or here.

You can also look at LINQ to XML (or at the 5 minute overview) to get some more ideas.

Solution 5

How about something along the lines of:

#light
open System.Xml

let xml = "..."

let emailList = 
    let xdoc = new XmlDocument()
    xdoc.LoadXml(xml)

    let mutable list = []
    let addEmail e = list <- e :: emailList

    xdoc.SelectNodes("//EmailList")
    |> IEnumerable.iter(fun(item:XmlNode) ->
        item
        |> IEnumerable.iter(fun(e:XmlNode) ->
            addEmail e.InnerText; ()))

    list
Share:
15,617

Related videos on Youtube

TonyAbell
Author by

TonyAbell

Updated on November 25, 2020

Comments

  • TonyAbell
    TonyAbell over 3 years

    this c# code is probably not the most efficient but gets what I want done.

    How do I accomplish the same thing in F# code?

        string xml = " <EmailList> " +
                   "      <Email>[email protected]</Email> " +
                   "      <Email>[email protected]</Email> " +
                   " </EmailList> ";
    
        XmlDocument xdoc = new XmlDocument();
        XmlNodeList nodeList;
        String emailList = string.Empty;
        xdoc.LoadXml(xml);
        nodeList = xdoc.SelectNodes("//EmailList");
        foreach (XmlNode item in nodeList)
        {
            foreach (XmlNode email in item)
            {
                 emailList +=  email.InnerText.ToString() +  Environment.NewLine ;
            }               
        }
    
  • Juliet
    Juliet over 15 years
    Is there any particular reason for using IEnumerable.iter rather than Seq.iter? There's no difference between the two, Seq is more idiomatic F#.
  • Frank Krueger
    Frank Krueger over 15 years
    Nope, other than I haven't programmed F# in a couple years. :-)