Xml in F#

Here’s the translation of the XML example from Go to F#:

open System
open System.Xml.Serialization
open System.IO

// Plant will be mapped to XML. We use attributes to specify
// XML serialization details. The XmlRoot attribute dictates
// the name of the XML element representing this type.
[<XmlRoot("plant")>]
type Plant = {
    [<XmlAttribute("id")>]
    Id: int
    [<XmlElement("name")>]
    Name: string
    [<XmlArray("origin")>]
    Origin: string[]
}

// ToString method for Plant
let plantToString (p: Plant) =
    sprintf "Plant id=%d, name=%s, origin=[%s]" p.Id p.Name (String.Join("; ", p.Origin))

[<EntryPoint>]
let main argv =
    let coffee = { Id = 27; Name = "Coffee"; Origin = [| "Ethiopia"; "Brazil" |] }

    // Emit XML representing our plant; using XmlSerializer
    // to produce a more human-readable output.
    let serializer = new XmlSerializer(typeof<Plant>)
    use writer = new StringWriter()
    serializer.Serialize(writer, coffee)
    let xmlOutput = writer.ToString()
    printfn "%s" xmlOutput

    // To add a generic XML header to the output, prepend it explicitly.
    printfn "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n%s" xmlOutput

    // Use XmlSerializer to parse a stream of bytes with XML
    // into a data structure.
    use reader = new StringReader(xmlOutput)
    let deserializedPlant = serializer.Deserialize(reader) :?> Plant
    printfn "%s" (plantToString deserializedPlant)

    let tomato = { Id = 81; Name = "Tomato"; Origin = [| "Mexico"; "California" |] }

    // The XmlArrayItem attribute tells the serializer to nest all
    // plants under <parent><child>...</child></parent>
    [<XmlRoot("nesting")>]
    type Nesting = {
        [<XmlArray("parent")>]
        [<XmlArrayItem("child")>]
        Plants: Plant[]
    }

    let nesting = { Plants = [| coffee; tomato |] }

    use writer = new StringWriter()
    let nestingSerializer = new XmlSerializer(typeof<Nesting>)
    nestingSerializer.Serialize(writer, nesting)
    printfn "%s" (writer.ToString())

    0

This F# code demonstrates XML serialization and deserialization using the System.Xml.Serialization namespace. Here’s a breakdown of the key points:

  1. We define a Plant record type with XML attributes to control serialization.

  2. The plantToString function provides a string representation of a Plant.

  3. In the main function, we create a coffee instance and serialize it to XML.

  4. We demonstrate how to add an XML header to the output.

  5. We then deserialize the XML back into a Plant instance.

  6. A Nesting type is defined to show how to create nested XML structures.

  7. Finally, we create a Nesting instance with both coffee and tomato plants and serialize it to XML.

To run this program, save it as a .fs file and compile it using the F# compiler (fsc). Then run the resulting executable.

Note that F# uses attributes (like [<XmlRoot>]) instead of struct tags to control XML serialization. The XmlSerializer class is used for both serialization and deserialization, which is similar to the encoding/xml package in Go.