5 people like it.

CSV writer

A simple CSV writer implementation as two type extensions for the Seq module. Use it with Records, Classes and Tuples. Have a look at the modified CSV reader sample from Don Symes Expert F# too http://fssnip.net/3T in order to advance this snippet using the ColumnAttribute This version adds quote enclosure support and support for seq datatype.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
32: 
33: 
34: 
35: 
36: 
37: 
38: 
39: 
40: 
41: 
42: 
43: 
44: 
45: 
46: 
47: 
48: 
49: 
50: 
51: 
52: 
53: 
54: 
55: 
56: 
57: 
58: 
59: 
module Csv

open System.IO
open Microsoft.FSharp.Reflection

type Array =
    static member join delimiter xs = 
        xs 
        |> Array.map (fun x -> x.ToString())
        |> String.concat delimiter

type Seq =
    static member write (path:string) (data:seq<'a>): 'result = 
        use writer = new StreamWriter(path)
        data
        |> Seq.iter writer.WriteLine 

    static member csv (separator:string) (useEnclosure:bool) (headerMapping:string -> string) ( data:seq<'a>) =
        seq {
            let dataType = typeof<'a>
            let stringSeqDataType = typeof<System.Collections.Generic.IEnumerable<string>>
            let inline enclose s =
                match useEnclosure with
                | true -> "\"" + (string s) + "\""
                | false -> string s

            let header = 
                match dataType with
                | ty when FSharpType.IsRecord ty ->
                    FSharpType.GetRecordFields dataType
                    |> Array.map (fun info -> headerMapping info.Name)                    
                | ty when FSharpType.IsTuple ty -> 
                    FSharpType.GetTupleElements dataType
                    |> Array.mapi (fun idx info -> headerMapping(string idx) )
                | ty when ty.IsAssignableFrom stringSeqDataType ->
                    data :?> seq<seq<string>> |> Seq.head
                    |> Seq.toArray
                | _ -> dataType.GetProperties()
                    |> Array.map (fun info -> headerMapping info.Name)

            yield header |> Array.map enclose |> Array.join separator
                                    
            let lines =
                match dataType with 
                | ty when FSharpType.IsRecord ty -> 
                    data |> Seq.map FSharpValue.GetRecordFields
                | ty when FSharpType.IsTuple ty ->
                    data |> Seq.map FSharpValue.GetTupleFields
                | ty when ty.IsAssignableFrom stringSeqDataType ->
                    data :?> seq<seq<string>> |> Seq.tail
                    |> Seq.map (fun ss -> Seq.toArray ss |> Array.map (fun s -> s :> obj) )
                | _ -> 
                    let props = dataType.GetProperties()
                    data |> Seq.map ( fun line -> 
                                props |> Array.map ( fun prop ->
                                prop.GetValue(line, null) ))                                     
                |> Seq.map (Array.map enclose)
            yield! lines |> Seq.map (Array.join separator)
        }
module Csv
namespace System
namespace System.IO
namespace Microsoft
namespace Microsoft.FSharp
namespace Microsoft.FSharp.Reflection
Multiple items
module Array

from Microsoft.FSharp.Collections

--------------------
type Array =
  static member join : delimiter:string -> xs:'a [] -> string

Full name: Csv.Array
static member Array.join : delimiter:string -> xs:'a [] -> string

Full name: Csv.Array.join
val delimiter : string
val xs : 'a []
val map : mapping:('T -> 'U) -> array:'T [] -> 'U []

Full name: Microsoft.FSharp.Collections.Array.map
val x : 'a
System.Object.ToString() : string
module String

from Microsoft.FSharp.Core
val concat : sep:string -> strings:seq<string> -> string

Full name: Microsoft.FSharp.Core.String.concat
Multiple items
module Seq

from Microsoft.FSharp.Collections

--------------------
type Seq =
  static member csv : separator:string -> useEnclosure:bool -> headerMapping:(string -> string) -> data:seq<'a> -> seq<string>
  static member write : path:string -> data:seq<'a> -> unit

Full name: Csv.Seq
static member Seq.write : path:string -> data:seq<'a> -> unit

Full name: Csv.Seq.write
val path : string
Multiple items
val string : value:'T -> string

Full name: Microsoft.FSharp.Core.Operators.string

--------------------
type string = System.String

Full name: Microsoft.FSharp.Core.string
val data : seq<'a>
Multiple items
val seq : sequence:seq<'T> -> seq<'T>

Full name: Microsoft.FSharp.Core.Operators.seq

--------------------
type seq<'T> = System.Collections.Generic.IEnumerable<'T>

Full name: Microsoft.FSharp.Collections.seq<_>
val writer : StreamWriter
Multiple items
type StreamWriter =
  inherit TextWriter
  new : stream:Stream -> StreamWriter + 6 overloads
  member AutoFlush : bool with get, set
  member BaseStream : Stream
  member Close : unit -> unit
  member Encoding : Encoding
  member Flush : unit -> unit
  member Write : value:char -> unit + 3 overloads
  static val Null : StreamWriter

Full name: System.IO.StreamWriter

--------------------
StreamWriter(stream: Stream) : unit
StreamWriter(path: string) : unit
StreamWriter(stream: Stream, encoding: System.Text.Encoding) : unit
StreamWriter(path: string, append: bool) : unit
StreamWriter(stream: Stream, encoding: System.Text.Encoding, bufferSize: int) : unit
StreamWriter(path: string, append: bool, encoding: System.Text.Encoding) : unit
StreamWriter(path: string, append: bool, encoding: System.Text.Encoding, bufferSize: int) : unit
val iter : action:('T -> unit) -> source:seq<'T> -> unit

Full name: Microsoft.FSharp.Collections.Seq.iter
TextWriter.WriteLine() : unit
   (+0 other overloads)
TextWriter.WriteLine(value: obj) : unit
   (+0 other overloads)
TextWriter.WriteLine(value: string) : unit
   (+0 other overloads)
TextWriter.WriteLine(value: decimal) : unit
   (+0 other overloads)
TextWriter.WriteLine(value: float) : unit
   (+0 other overloads)
TextWriter.WriteLine(value: float32) : unit
   (+0 other overloads)
TextWriter.WriteLine(value: uint64) : unit
   (+0 other overloads)
TextWriter.WriteLine(value: int64) : unit
   (+0 other overloads)
TextWriter.WriteLine(value: uint32) : unit
   (+0 other overloads)
TextWriter.WriteLine(value: int) : unit
   (+0 other overloads)
static member Seq.csv : separator:string -> useEnclosure:bool -> headerMapping:(string -> string) -> data:seq<'a> -> seq<string>

Full name: Csv.Seq.csv
val separator : string
val useEnclosure : bool
type bool = System.Boolean

Full name: Microsoft.FSharp.Core.bool
val headerMapping : (string -> string)
val dataType : System.Type
val typeof<'T> : System.Type

Full name: Microsoft.FSharp.Core.Operators.typeof
val stringSeqDataType : System.Type
namespace System.Collections
namespace System.Collections.Generic
type IEnumerable<'T> =
  member GetEnumerator : unit -> IEnumerator<'T>

Full name: System.Collections.Generic.IEnumerable<_>
val enclose : ('a -> string)
val s : 'a
val header : string []
val ty : System.Type
type FSharpType =
  static member GetExceptionFields : exceptionType:Type * ?bindingFlags:BindingFlags -> PropertyInfo []
  static member GetFunctionElements : functionType:Type -> Type * Type
  static member GetRecordFields : recordType:Type * ?bindingFlags:BindingFlags -> PropertyInfo []
  static member GetTupleElements : tupleType:Type -> Type []
  static member GetUnionCases : unionType:Type * ?bindingFlags:BindingFlags -> UnionCaseInfo []
  static member IsExceptionRepresentation : exceptionType:Type * ?bindingFlags:BindingFlags -> bool
  static member IsFunction : typ:Type -> bool
  static member IsModule : typ:Type -> bool
  static member IsRecord : typ:Type * ?bindingFlags:BindingFlags -> bool
  static member IsTuple : typ:Type -> bool
  ...

Full name: Microsoft.FSharp.Reflection.FSharpType
static member FSharpType.IsRecord : typ:System.Type * ?allowAccessToPrivateRepresentation:bool -> bool
static member FSharpType.IsRecord : typ:System.Type * ?bindingFlags:System.Reflection.BindingFlags -> bool
static member FSharpType.GetRecordFields : recordType:System.Type * ?allowAccessToPrivateRepresentation:bool -> System.Reflection.PropertyInfo []
static member FSharpType.GetRecordFields : recordType:System.Type * ?bindingFlags:System.Reflection.BindingFlags -> System.Reflection.PropertyInfo []
val info : System.Reflection.PropertyInfo
property System.Reflection.MemberInfo.Name: string
static member FSharpType.IsTuple : typ:System.Type -> bool
static member FSharpType.GetTupleElements : tupleType:System.Type -> System.Type []
val mapi : mapping:(int -> 'T -> 'U) -> array:'T [] -> 'U []

Full name: Microsoft.FSharp.Collections.Array.mapi
val idx : int
val info : System.Type
System.Type.IsAssignableFrom(c: System.Type) : bool
val head : source:seq<'T> -> 'T

Full name: Microsoft.FSharp.Collections.Seq.head
val toArray : source:seq<'T> -> 'T []

Full name: Microsoft.FSharp.Collections.Seq.toArray
System.Type.GetProperties() : System.Reflection.PropertyInfo []
System.Type.GetProperties(bindingAttr: System.Reflection.BindingFlags) : System.Reflection.PropertyInfo []
static member Array.join : delimiter:string -> xs:'a [] -> string
val lines : seq<string []>
val map : mapping:('T -> 'U) -> source:seq<'T> -> seq<'U>

Full name: Microsoft.FSharp.Collections.Seq.map
type FSharpValue =
  static member GetExceptionFields : exn:obj * ?bindingFlags:BindingFlags -> obj []
  static member GetRecordField : record:obj * info:PropertyInfo -> obj
  static member GetRecordFields : record:obj * ?bindingFlags:BindingFlags -> obj []
  static member GetTupleField : tuple:obj * index:int -> obj
  static member GetTupleFields : tuple:obj -> obj []
  static member GetUnionFields : value:obj * unionType:Type * ?bindingFlags:BindingFlags -> UnionCaseInfo * obj []
  static member MakeFunction : functionType:Type * implementation:(obj -> obj) -> obj
  static member MakeRecord : recordType:Type * values:obj [] * ?bindingFlags:BindingFlags -> obj
  static member MakeTuple : tupleElements:obj [] * tupleType:Type -> obj
  static member MakeUnion : unionCase:UnionCaseInfo * args:obj [] * ?bindingFlags:BindingFlags -> obj
  ...

Full name: Microsoft.FSharp.Reflection.FSharpValue
static member FSharpValue.GetRecordFields : record:obj * ?allowAccessToPrivateRepresentation:bool -> obj []
static member FSharpValue.GetRecordFields : record:obj * ?bindingFlags:System.Reflection.BindingFlags -> obj []
static member FSharpValue.GetTupleFields : tuple:obj -> obj []
val tail : source:seq<'T> -> seq<'T>

Full name: Microsoft.FSharp.Collections.Seq.tail
val ss : seq<string>
val s : string
type obj = System.Object

Full name: Microsoft.FSharp.Core.obj
val props : System.Reflection.PropertyInfo []
val line : 'a
val prop : System.Reflection.PropertyInfo
System.Reflection.PropertyInfo.GetValue(obj: obj, index: obj []) : obj
System.Reflection.PropertyInfo.GetValue(obj: obj, invokeAttr: System.Reflection.BindingFlags, binder: System.Reflection.Binder, index: obj [], culture: System.Globalization.CultureInfo) : obj

More information

Link:http://fssnip.net/3U
Posted:7 years ago
Author:Rainer Schuster
Tags: csv , serialize , writer