鉴于这些类型和本单元测试:
type DiscUnion =
| D1
| D2
type Foo =
{
Bar: int;
Baz: Map<DiscUnion,int>;
}
let ImportDiscUnionJson (json: string): Foo =
Marshalling.Deserialize json
let DiscUnionExampleInJson =
"{\"Bar\": 42, \"Baz\":" +
"{\"D1\": 4242, " +
"\"D2\": 424242 }}"
[<Test>]
let ``testing disc union deserialization``() =
let deserializedDiscUnion =
ImportDiscUnionJson
DiscUnionExampleInJson
Assert.That(deserializedDiscUnion, Is.Not.Null)
Assert.That(deserializedDiscUnion.Bar,
Is.EqualTo(42))
Assert.That(deserializedDiscUnion.Baz.[DiscUnion.D1],
Is.EqualTo(4242))
Assert.That(deserializedDiscUnion.Baz.[DiscUnion.D2],
Is.EqualTo(424242))
我从Newtonsoft.Json(JSON.NET,我使用版本9.0.1)收到此异常:
----&GT; Newtonsoft.Json.JsonSerializationException:无法转换 字符串&#39; D1&#39;字典键类型 &#39; FSharpTests.Deserialization + DiscUnion&#39 ;.创建一个 TypeConverter将字符串转换为键类型对象。路径 &#39; Value.Baz.D1&#39;,第1行,第82位.----&gt; Newtonsoft.Json.JsonSerializationException:转换值时出错 &#34; D1&#34;输入&#39; FSharpTests.Deserialization + DiscUnion&#39;。路径 &#39; Value.Baz.D1&#39;,第1行,第82位。
然后我通过这种方式向类型添加了一个TypeConverter:
let Construct<'T> (caseInfo: Microsoft.FSharp.Reflection.UnionCaseInfo) =
Microsoft.FSharp.Reflection.FSharpValue.MakeUnion(caseInfo, [||]) :?> 'T
let GetUnionCaseInfoAndInstance<'T> (caseInfo: Microsoft.FSharp.Reflection.UnionCaseInfo) =
(Construct<'T> caseInfo)
let GetAllElementsFromDiscriminatedUnion<'T>() =
Microsoft.FSharp.Reflection.FSharpType.GetUnionCases(typeof<'T>)
|> Seq.map GetUnionCaseInfoAndInstance<'T>
[<System.ComponentModel.TypeConverter(typeof<MyStringTypeConverter>)>]
type DiscUnion =
| D1
| D2
override self.ToString() =
sprintf "%A" self
static member GetAll(): seq<DiscUnion> =
GetAllElementsFromDiscriminatedUnion<DiscUnion>()
and private MyStringTypeConverter() =
inherit System.ComponentModel.TypeConverter()
override this.CanConvertFrom(context, sourceType) =
sourceType = typedefof<string> || base.CanConvertFrom(context, sourceType)
override this.ConvertFrom(context, culture, value) =
match value with
| :? string as stringValue ->
Seq.find (fun discUnion -> discUnion.ToString() = stringValue) (DiscUnion.GetAll()) :> obj
| _ -> base.ConvertFrom(context, culture, value)
并且效果很好,但是,TypeConverter类型并不存在于PCL配置文件中(我猜是System.ComponentModel
没有),所以我尝试使用JsonConverter:
let Construct<'T> (caseInfo: Microsoft.FSharp.Reflection.UnionCaseInfo) =
Microsoft.FSharp.Reflection.FSharpValue.MakeUnion(caseInfo, [||]) :?> 'T
let GetUnionCaseInfoAndInstance<'T> (caseInfo: Microsoft.FSharp.Reflection.UnionCaseInfo) =
(Construct<'T> caseInfo)
let GetAllElementsFromDiscriminatedUnion<'T>() =
Microsoft.FSharp.Reflection.FSharpType.GetUnionCases(typeof<'T>)
|> Seq.map GetUnionCaseInfoAndInstance<'T>
[<JsonConverter(typeof<MyStringTypeConverter>)>]
type DiscUnion =
| D1
| D2
override self.ToString() =
sprintf "%A" self
static member GetAll(): seq<DiscUnion> =
GetAllElementsFromDiscriminatedUnion<DiscUnion>()
and private MyStringTypeConverter() =
inherit JsonConverter()
override this.CanConvert(objectType): bool =
objectType = typedefof<DiscUnion>
override this.ReadJson(reader: JsonReader, objectType: Type, existingValue: Object, serializer: JsonSerializer) =
if (reader.TokenType = JsonToken.Null) then
null
else
let token =
Newtonsoft.Json.Linq.JToken.Load(reader)
// not sure about the below way to convert to string, in stackoverflow it was a C# cast
.ToString()
try
DiscUnion.GetAll().First(fun discUnion -> discUnion.ToString() = token) :> Object
with ex -> raise(new Exception(sprintf "DiscUnion case not found: %s" token, ex))
override this.WriteJson(writer: JsonWriter, value: Object, serializer: JsonSerializer) =
let discUnion = value :?> DiscUnion
writer.WriteValue(discUnion.ToString())
但这不起作用,我仍然得到以前的例外:
----&GT; Newtonsoft.Json.JsonSerializationException:无法转换 字符串&#39; D1&#39;字典键类型 &#39; FSharpTests.Deserialization + DiscUnion&#39 ;.创建一个 TypeConverter将字符串转换为键类型对象。路径 &#39; Value.Baz.D1&#39;,第1行,第82位.----&gt; Newtonsoft.Json.JsonSerializationException:转换值时出错 &#34; D1&#34;输入&#39; FSharpTests.Deserialization + DiscUnion&#39;。路径 &#39; Value.Baz.D1&#39;,第1行,第82位。
如何解决这个问题仍然与PCL兼容?
答案 0 :(得分:0)
我最终使用.NETStandard2.0而不是PCL