レコード型や選択型のマップを使ったシリアライズ
LT;DR
JSON にシリアライズすることが決まっている場合など
これだけで radish-miyazaki.icon はこの選択肢は無くなった…
適度な結合が大事
hr.icon
レコード型 や 判別共用体 の別のシリアライズ方法として、すべてキーバリューマップとしてシリアライズする方法がある JSON にシリアライズする場合はこれが適している
具体的には、DTO を IDictonary<string,obj> として表現する
契約が無く
IDictonary<string,obj> には任意の値を追加できるため
デメリット: 契約がまったくないこと
適度な結合が大事
契約がまったくないとどうなるのかを見る
再掲
code:domain.fs
type Name =
{ First: String50
Last: String50 }
type Example =
| A
| B of int
| C of string list
| D of Name
code:fsharp
let nameDtoFromDomain (name: Domain.Name) : IDictionary<string, obj> =
let first = name.First |> String50.value :> obj
let last = name.Last |> String50.value :> obj
warning.icon レコード内のすべての値は、アップキャスト 演算子 :> を用いて obj にキャストする必要がある これが「契約がまったくない」ことを指す
Example のシリアライズ
code:fsharp
let fromDomain (domainObj: Domain.Example) : IDictionary<string, obj> =
match domainObj with
| B i ->
let bdata = Nullable i :> obj
| C strList ->
let cdata = strList |> List.toArray :> obj
| D name ->
let ddata = name |> nameDtoFromDomain :> obj
それぞれのフィールドについて以下が必要
1. 値を調べて存在するかチェックする TryGetValue
2. 1. で値があれば、正しい型にキャストする :?>(ダウンキャスト 演算子) はじめに、これを実現するヘルパ関数を実装する
code:fsharp
let getValue key (dict: IDictionary<string, obj>) : Result<'a, string> =
match dict.TryGetValue key with
| (true, value) -> // キーが見つかった
try
// 'a にダウンキャストして Ok にラップ
(value :?> 'a) |> Ok
with :? InvalidCastException ->
// キャストに失敗
let typeName = typeof<'a>.Name
let msg = sprintf "Value could not be cast to %s" typeName
Error msg
| (false, _) -> // キーが見つからなかった
let msg = sprintf "Key '%s' not found" key
Error msg
Name の デシリアライズ
code:fsharp
let nameDtoToDomain (nameDto: IDictionary<string, obj>) : Result<Name, string> =
result {
let! firstStr = nameDto |> getValue "First"
let! first = firstStr |> String50.create "First"
let! lastStr = nameDto |> getValue "Last"
let! last = lastStr |> String50.create "Last"
return { First = first; Last = last }
}
VSCode 上だと firstStr の型が string に推測されてる...! radish-miyazaki.icon
https://scrapbox.io/files/66b3604eb977a5001dd88977.png
first の式から再帰的に型が決まってそう ?? Example のデシリアライズ
各ケース毎にキーが存在するかどうかチェックする
code:fsharp
let toDomain (dto: IDictionary<string, obj>) : Result<Example, string> =
if dto.ContainsKey "A" then
Ok A
elif dto.ContainsKey "B" then
result {
let! bdata = dto |> getValue "B" // 失敗する可能性がある
return B bdata
}
elif dto.ContainsKey "C" then
result {
let! cdata = dto |> getValue "C" // 失敗する可能性がある
return cdata |> Array.toList |> C
}
elif dto.ContainsKey "D" then
result {
let! ddata = dto |> getValue "D" // 失敗する可能性がある
let! name = ddata |> nameDtoToDomain // ここも失敗する可能性がある
return name |> D
}
else
let msg = sprintf "Tag not found in dictionary"
Error msg