Enjoy!! (^_^)/
CSV
Name,Price,Color
apple,350,red
banana,250,yellow
cherry,450,redblack
result
seq [{ Name = "apple"
Price = 350
Color = "red" }]
seq
["Name:letters is over 5.;行 3;banana,250,yellow";
"Name:letters is over 5.;行 4;cherry,450,redblack"]
fsharp code
module ClassRecords
module MyValidate =
open System.Text
open FSharp.Core
let either fOk fError = function
| Ok x -> fOk x
| Error x -> fError x
let either2 f = function
| Ok x -> f x
| Error x -> f x
let (|OverLen|_|) n = function
| (s:string) when s.Length > n -> Some s
| _ -> None
let guardOverLen label (sb:StringBuilder) n = function
| OverLen n s -> Error(sb.Append($"{label}:letters is over {n}.") |> ignore ; s)
| s -> Ok s
open System.Collections.Concurrent
open System.Globalization
open System.IO
open CsvHelper
open CsvHelper.Configuration
exception MyCsvException of string
[<CLIMutableAttribute>]
type MyCsv = {Name:string;Price:int;Color:string }
type MyCsvMap () as this =
inherit ClassMap<MyCsv>()
let sb = System.Text.StringBuilder()
let csv = Unchecked.defaultof<MyCsv>
let dic = Microsoft.FSharp.Reflection.FSharpType.GetRecordFields(typeof<MyCsv>) |> Seq.mapi(fun idx x -> x.Name,idx) |> Map.ofSeq
let fieldValue (rObj:CsvHelper.ConvertFromStringArgs) columnName = ( columnName |> dic.TryFind |> fun x -> x.Value) |> rObj.Row.GetField<'T>
do
// validate all fields at first column
this.Map(fun x -> x.Name)
|> fun x -> x.Index(0)
|> fun x -> x.Convert(this.RowFieldsValidate)
|> ignore
this.Map(fun x -> x.Price).Index(1) |> ignore
this.Map(fun x -> x.Color).Index(2) |> ignore
member this.RowFieldsValidate:CsvHelper.ConvertFromStringArgs -> string =
fun rowObj ->
sb.Clear() |> ignore
(fieldValue rowObj (nameof(csv.Name)))
|> MyValidate.guardOverLen "Name" sb 5
|> ignore
if sb.ToString() <> ""
then raise <| MyCsvException (sb.ToString())
(fieldValue rowObj (nameof(csv.Name)))
let cq = new ConcurrentQueue<MyCsv>()
let cqBad = new ConcurrentQueue<string>()
let readCsv () =
let config = CsvConfiguration(CultureInfo.CurrentCulture)
config.NewLine <- "\n"
config.HasHeaderRecord <- true
let csv = new CsvReader( new StreamReader("./sample.csv") , config)
csv.Context.RegisterClassMap<MyCsvMap>() |> ignore
// Reading by hand
let mutable flg = true
while flg do
try
flg <- csv.Read()
if flg
then
cq.Enqueue(csv.GetRecord<MyCsv>())
with
| :? CsvHelperException as e ->
match e.InnerException with
| :? MyCsvException as e ->
cqBad.Enqueue(
e.Data0
+ ";行 " + (csv.Context.Parser.RawRow.ToString())
+ ";" + (csv.Context.Parser.RawRecord)
)
| _ -> cqBad.Enqueue( e.Message )
| e ->
cqBad.Enqueue( e.Message )