问题出在JSON.NET的DU序列化实现中。 It's not idiomatic,实际上是转储案例和字段:
type Shape =
| Rectangle of width : float * length : float
| Circle of radius : float
| Empty
[<EntryPoint>]
let main argv =
let shape1 = Rectangle(1.3,10.0)
let json = JsonConvert.SerializeObject(shape1)
// {
// "Case": "Rectangle",// "Fields": [
// 1.3,// 10.0
// ]
// }
解串器期望相同的结构。
艾萨克·亚伯拉罕(Isaac Abraham)创建了an idiomatic custom converter,应该改用它:
let settings = new JsonSerializerSettings()
settings.Converters.Add(IdiomaticDuConverter())
let t = JsonConvert.DeserializeObject<TradeContainer>(json,settings)
IdiomaticDuConverter
的代码是:
namespace Newtonsoft.Json.Converters
open Microsoft.FSharp.Reflection
open Newtonsoft.Json
open System
type IdiomaticDuConverter() =
inherit JsonConverter()
[<Literal>]
let discriminator = "__Case"
let primitives = Set [ JsonToken.Boolean; JsonToken.Date; JsonToken.Float; JsonToken.Integer; JsonToken.Null; JsonToken.String ]
let writeValue (value:obj) (serializer:JsonSerializer,writer : JsonWriter) =
if value.GetType().IsPrimitive then writer.WriteValue value
else serializer.Serialize(writer,value)
let writeProperties (fields : obj array) (serializer:JsonSerializer,writer : JsonWriter) =
fields |> Array.iteri (fun index value ->
writer.WritePropertyName(sprintf "Item%d" index)
(serializer,writer) |> writeValue value)
let writeDiscriminator (name : string) (writer : JsonWriter) =
writer.WritePropertyName discriminator
writer.WriteValue name
override __.WriteJson(writer,value,serializer) =
let unionCases = FSharpType.GetUnionCases(value.GetType())
let unionType = value.GetType()
let case,fields = FSharpValue.GetUnionFields(value,unionType)
let allCasesHaveValues = unionCases |> Seq.forall (fun c -> c.GetFields() |> Seq.length > 0)
match unionCases.Length,fields,allCasesHaveValues with
| 2,[||],false -> writer.WriteNull()
| 1,[| singleValue |],_
| 2,false -> (serializer,writer) |> writeValue singleValue
| 1,false ->
writer.WriteStartObject()
(serializer,writer) |> writeProperties fields
writer.WriteEndObject()
| _ ->
writer.WriteStartObject()
writer |> writeDiscriminator case.Name
(serializer,writer) |> writeProperties fields
writer.WriteEndObject()
override __.ReadJson(reader,destinationType,_,_) =
let parts =
if reader.TokenType <> JsonToken.StartObject then [| (JsonToken.Undefined,obj()),(reader.TokenType,reader.Value) |]
else
seq {
yield! reader |> Seq.unfold (fun reader ->
if reader.Read() then Some((reader.TokenType,reader.Value),reader)
else None)
}
|> Seq.takeWhile(fun (token,_) -> token <> JsonToken.EndObject)
|> Seq.pairwise
|> Seq.mapi (fun id value -> id,value)
|> Seq.filter (fun (id,_) -> id % 2 = 0)
|> Seq.map snd
|> Seq.toArray
let values =
parts
|> Seq.filter (fun ((_,keyValue),_) -> keyValue <> (discriminator :> obj))
|> Seq.map snd
|> Seq.filter (fun (valueToken,_) -> primitives.Contains valueToken)
|> Seq.map snd
|> Seq.toArray
let case =
let unionCases = FSharpType.GetUnionCases(destinationType)
let unionCase =
parts
|> Seq.tryFind (fun ((_,_) -> keyValue = (discriminator :> obj))
|> Option.map (snd >> snd)
match unionCase with
| Some case -> unionCases |> Array.find (fun f -> f.Name :> obj = case)
| None ->
// implied union case
match values with
| [| null |] -> unionCases |> Array.find(fun c -> c.GetFields().Length = 0)
| _ -> unionCases |> Array.find(fun c -> c.GetFields().Length > 0)
let values =
case.GetFields()
|> Seq.zip values
|> Seq.map (fun (value,propertyInfo) -> Convert.ChangeType(value,propertyInfo.PropertyType))
|> Seq.toArray
FSharpValue.MakeUnion(case,values)
override __.CanConvert(objectType) =
FSharpType.IsUnion objectType &&
not (objectType.IsGenericType &&
typedefof<list<_>> = objectType.GetGenericTypeDefinition())
本文链接:https://www.f2er.com/3144812.html