TypeConverter适用于Newtonsoft.Json,但不适用于JsonConverter吗? [F#)

问题描述 投票:4回答:1

鉴于这些类型和本单元测试:

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版)收到此异常:

----> Newtonsoft.Json.JsonSerializationException:无法转换字符串“ D1”到字典键类型'FSharpTests.Deserialization + DiscUnion'。创建一个TypeConverter,用于从字符串转换为键类型对象。路径'Value.Baz.D1',第1行,位置82。---->Newtonsoft.Json.JsonSerializationException:转换值时出错“ D1”以键入“ FSharpTests.Deserialization + DiscUnion”。路径'Value.Baz.D1',第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)

而且效果很好,但是PCL配置文件中不存在TypeConverter类型(我想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())

但是这不起作用,我仍然遇到先前的异常:

----> Newtonsoft.Json.JsonSerializationException:无法转换字符串“ D1”到字典键类型'FSharpTests.Deserialization + DiscUnion'。创建一个TypeConverter,用于从字符串转换为键类型对象。路径'Value.Baz.D1',第1行,位置82。---->Newtonsoft.Json.JsonSerializationException:转换值时出错“ D1”以键入“ FSharpTests.Deserialization + DiscUnion”。路径'Value.Baz.D1',第1行,位置82。

如何解决此问题并仍然兼容PCL?

.net json f# portable-class-library
1个回答
0
投票

我最终使用了.NETStandard2.0而不是PCL

© www.soinside.com 2019 - 2024. All rights reserved.