Mathias Weyel
Mathias Weyel

Reputation: 819

Fsharp.Data: Optional Elements in XML

Within a sample xml file for the F#-Data type provider, I have elements that are optional, like so:

<RootElement>
    <MandatoryElement> ... </MandatoryElement>
    <OptionalElement> ... </OptionalElement>
    <AnotherElement> ... </AnotherElement>
</RootElement>

I don't know how to specify the OptionalElement as optional. There can be only one RootElement so I cannot add another one lacking the OptionalElement. How can I tell the parser, that OptionalElement is actually optional?

Upvotes: 1

Views: 367

Answers (2)

Mark Seemann
Mark Seemann

Reputation: 233277

The XML Type Provider works by inferring the type from the sample. You can provide more than one sample by using the optional SampleIsList argument:

open FSharp.Data

type RootElement = XmlProvider<"""
<samples>
    <RootElement>
        <MandatoryElement> ... </MandatoryElement>
        <OptionalElement> ... </OptionalElement>
        <AnotherElement> ... </AnotherElement>
    </RootElement>
    <RootElement>
        <MandatoryElement> ... </MandatoryElement>
        <AnotherElement> ... </AnotherElement>
    </RootElement>
</samples>""", SampleIsList = true>

From this list of samples, the XML Type Provder infers that OptionalElement is, well... optional, and types it as a string option:

let x = RootElement.Parse """
    <RootElement>
        <MandatoryElement> ... </MandatoryElement>
        <OptionalElement> ... </OptionalElement>
        <AnotherElement> ... </AnotherElement>
    </RootElement>"""
let y = RootElement.Parse """
    <RootElement>
        <MandatoryElement> ... </MandatoryElement>
        <AnotherElement> ... </AnotherElement>
    </RootElement>"""

Usage:

> y.OptionalElement.IsSome;;
val it : bool = false
> x.OptionalElement.IsSome;;
val it : bool = true
> x.OptionalElement |> Option.get;;
val it : string = " ... "

Upvotes: 1

Fyodor Soikin
Fyodor Soikin

Reputation: 80805

There is no way to explicitly specify "optional" within XML itself. The XML type provider infers this if it sees the element in some places, but not others, but that is only an educated guess.

For explicitly, strictly specifying which elements are optional, which can be multiple, etc., we have something called "XML Schema", also known as "XSD". Unfortunately, the XML type provider does not support XSD at the moment, although there is an open issue for it.

One hack I can offer you is this: make your root element nested under another, "super-root" element, and then make two of the "real root" ones, which will let the type provider infer the optional-ness. The type provider will then generate a "super-root" type for you, which you can promptly disregard, and only use the nested one, the real root.

Of course, since XML Type Provider, sadly, doesn't support parsing non-root elements, you will also have to "wrap" the XML text in the "super-root" element every time you parse it, which limits the solution to small documents only.

type Xml = XmlProvider<"""
  <SuperRoot>
    <RootElement>
        <MandatoryElement> ... </MandatoryElement>
        <OptionalElement> ... </OptionalElement>
        <AnotherElement> ... </AnotherElement>
    </RootElement>
    <RootElement>
        <MandatoryElement> ... </MandatoryElement>
        <AnotherElement> ... </AnotherElement>
    </RootElement>
  </SuperRoot>
""">

let parse xml = (Xml.Parse ("<SuperRoot>" + xml + "</SuperRoot>")).RootElements.[0]

Upvotes: 0

Related Questions