Thursday, August 4, 2011

Adding xml strings in F# using computation expressions

In some cases types are enhanced wrappers of simple types. For instance in .NET:

  • DateTime is a point in time and provides, among others things, extra functionality related to representing date and time formats.
  • XElement is an xml string and provides, among others things, checks to determine the validity of the xml string.
  • Option can contain a value and can tell you if there is value available.

Sometimes you have to deal with underling type and reuse some of the functionality of the wrapper. In this case you could consider using computation expressions.

This is a simple xml example. We create a xmlBuilder and reuse the validation logic of XElement:

open System
open System.Xml
open System.Xml.Linq

type xmlBuilder()=
member x.Bind(el:XElement,(f:string -> XElement)) =
try
f(el.ToString())
with
| :? XmlException as exc -> XElement.Parse(String.Format("<XmlException>{0}</XmlException>", exc.Message))
member x.Return(text) =
try
XElement.Parse(text)
with
| :? XmlException as exc -> XElement.Parse(String.Format("<XmlException>{0}</XmlException>", exc.Message))

The string “123” creates an error:
let xml = new xmlBuilder()

let error =
xml {
return "123"
}
val error : XElement = <XmlException>Data at the root level is invalid. Line 1, position 1.</XmlException>

The expression can now be used to reuse sting functions:

let toUpper el =
xml {
let! x = el
return x.ToUpper()
}

let el = XElement.Parse("<a><b>1</b><b>2</b></a>")
let result1 = el |> toUpper

val result1 : XElement = <A>
<B>1</B>
<B>2</B>
</A>

In the same way we can define a simple add function for xml:

let (+) (el1:XElement) (el2:XElement) =
xml {
let! x1 = el1
let! x2 = el2
return x1.Substring(0, x1.LastIndexOf("<")) + x2.Substring(x2.IndexOf(">") + 1)
}

let el1 = XElement.Parse("<a><b>1</b><b>2</b></a>")
let el2 = XElement.Parse("<a><b>3</b><b>4</b></a>")
let result2 = el1 + el2

val result2 : XElement =
<a>
<b>1</b>
<b>2</b>
<b>3</b>
<b>4</b>
</a>

let el3 = XElement.Parse("<c><b>3</b><b>4</b></c>")
let result3 = el1 + el3

val result3 : XElement = <XmlException>The 'a' start tag on line 1 position 2 does not match the end tag of 'c'. Line 7, position 3.</XmlException>
This may not be in line with the concept of information hiding, but it can help you in case you have to deal with the simple type to get the work done.

No comments:

Total Pageviews