用Json.Net反序列化F#Map

用Json.Net反序列化F#Map,json,serialization,f#,json.net,Json,Serialization,F#,Json.net,我有一个简单的问题:是否可以从中解析F#映射类型?因为当我尝试它时(使用F#Map),它很容易序列化,并且看起来它必须序列化,但是当我尝试反序列化时,它抛出了一个异常 Newtonsoft.Json.JsonSerializationException: Unable to find a default constructor to use for type Microsoft.FSharp.Collections.FSharpMap`2[System.Int32,System.String].

我有一个简单的问题:是否可以从中解析F#映射类型?因为当我尝试它时(使用
F#Map
),它很容易序列化,并且看起来它必须序列化,但是当我尝试反序列化时,它抛出了一个异常

Newtonsoft.Json.JsonSerializationException: Unable to find a default constructor to use for type Microsoft.FSharp.Collections.FSharpMap`2[System.Int32,System.String]. Path '1', line 2, position 7.
  at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateNewDictionary (Newtonsoft.Json.JsonReader reader, Newtonsoft.Json.Serialization.JsonDictionaryContract contract, System.Boolean& createdFromNonDefaultConstructor) [0x00000] in <filename unknown>:0 
  at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject (Newtonsoft.Json.JsonReader reader, System.Type objectType, Newtonsoft.Json.Serialization.JsonContract contract, Newtonsoft.Json.Serialization.JsonProperty member, Newtonsoft.Json.Serialization.JsonContainerContract containerContract, Newtonsoft.Json.Serialization.JsonProperty containerMember, System.Object existingValue) [0x00000] in <filename unknown>:0 
  at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal (Newtonsoft.Json.JsonReader reader, System.Type objectType, Newtonsoft.Json.Serialization.JsonContract contract, Newtonsoft.Json.Serialization.JsonProperty member, Newtonsoft.Json.Serialization.JsonContainerContract containerContract, Newtonsoft.Json.Serialization.JsonProperty containerMember, System.Object existingValue) [0x00000] in <filename unknown>:0 
  at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize (Newtonsoft.Json.JsonReader reader, System.Type objectType, Boolean checkAdditionalContent) [0x00000] in <filename unknown>:0
生成的JSON看起来像C#dictionary

{
“1”:“一”,
"二":"二",,
“3”:“三”
}
它正在序列化,没有设置(仅缩进)。那么,是否有可能将其序列化,或者是否有一些解决方法


感谢您的回答

问题是json.net无法构建
映射
。但是,如果反序列化到一个常规的.net
字典
,它会工作,因为json是相同的。

您不能直接序列化F#的映射,因为它根本没有默认构造函数(没有参数的构造函数)

这是F#map的原始文档:(来自)

[]
类型图=
班
接口IEnumerable
接口不可比较
接口IEnumerable
接口ICollection
接口词典
新映射:seq->Map<'Key',Value>
添加:'Key*'值->映射
成员this.ContainsKey:'Key->bool
成员。删除:'Key->Map
成员this.TryFind:'Key->'值选项
这个成员。计数:int
成员:我是空的:bool
成员。项('键):'值
结束
如上所述,Map没有默认构造函数,但序列化程序需要一个具有默认构造函数的类


序列化映射的最佳方法是将映射映射到常规的.NET字典,但新字典并没有F映射的所有优点,特别是F映射的不变性。

您可以制作自己的转换器来实现这一点。这需要大量的反射和构造适当的泛型类型,但这是可以做到的

首先反序列化到
字典
,然后通过反射手动创建并填充
列表
(因为
映射
构造函数需要
元组
,而不是
键值对
),最后将其传递到
映射
构造函数

不确定是否有更简单的方法,但这就是我想到的:

open System
open System.Collections
open System.Collections.Generic
open Newtonsoft.Json

let mapConverter = {
  new JsonConverter() with

    override x.CanConvert(t:Type) =
      t.IsGenericType && t.GetGenericTypeDefinition() = typedefof<Map<_, _>>

    override x.WriteJson(writer, value, serializer) =
      serializer.Serialize(writer, value)

    override x.ReadJson(reader, t, _, serializer) =
      let genArgs = t.GetGenericArguments()
      let generify (t:Type) = t.MakeGenericType genArgs
      let tupleType = generify typedefof<Tuple<_, _>>
      let listType = typedefof<List<_>>.MakeGenericType tupleType
      let create (t:Type) types = (t.GetConstructor types).Invoke
      let list = create listType [||] [||] :?> IList
      let kvpType = generify typedefof<KeyValuePair<_, _>>
      for kvp in serializer.Deserialize(reader, generify typedefof<Dictionary<_, _>>) :?> IEnumerable do
        let get name = (kvpType.GetProperty name).GetValue(kvp, null)
        list.Add (create tupleType genArgs [|get "Key"; get "Value"|]) |> ignore        
      create (generify typedefof<Map<_, _>>) [|listType|] [|list|]
}

这样做的好处是,如果你有一个大/深的记录,其中你的
地图
只是一个字段,然后它也可以使用了——您不必为了支持序列化而更改记录结构来使用
字典。

此功能成为6.0.3版JSON.Net的一部分。(2014年4月30日)

但是,如果您因某种原因无法使用早期版本,那么Dax Fohl版本的简化版(更高效,因为反射更少)可能是:


type mapconvert我想这是一个很严重的问题,因为F#map是不可变的。如果您发现自己稍微调用了MakeGenericType,那么创建一个帮助器类型来简化事情就很方便了-请参阅
open System
open System.Collections
open System.Collections.Generic
open Newtonsoft.Json

let mapConverter = {
  new JsonConverter() with

    override x.CanConvert(t:Type) =
      t.IsGenericType && t.GetGenericTypeDefinition() = typedefof<Map<_, _>>

    override x.WriteJson(writer, value, serializer) =
      serializer.Serialize(writer, value)

    override x.ReadJson(reader, t, _, serializer) =
      let genArgs = t.GetGenericArguments()
      let generify (t:Type) = t.MakeGenericType genArgs
      let tupleType = generify typedefof<Tuple<_, _>>
      let listType = typedefof<List<_>>.MakeGenericType tupleType
      let create (t:Type) types = (t.GetConstructor types).Invoke
      let list = create listType [||] [||] :?> IList
      let kvpType = generify typedefof<KeyValuePair<_, _>>
      for kvp in serializer.Deserialize(reader, generify typedefof<Dictionary<_, _>>) :?> IEnumerable do
        let get name = (kvpType.GetProperty name).GetValue(kvp, null)
        list.Add (create tupleType genArgs [|get "Key"; get "Value"|]) |> ignore        
      create (generify typedefof<Map<_, _>>) [|listType|] [|list|]
}
let str = JsonConvert.SerializeObject (Map<_, _> [333, 1234])
JsonConvert.DeserializeObject<Map<int, int>>(str, mapConverter)
type mapConvert<'f,'t when 'f : comparison>() =
    static member readJson (reader:JsonReader, serializer:JsonSerializer) =
        serializer.Deserialize<Dictionary<'f, 't>> (reader)
        |> Seq.map (fun kv -> kv.Key, kv.Value)
        |> Map.ofSeq

let mapConverter = {
  new JsonConverter() with
    override __.CanConvert (t:Type) =
      t.IsGenericType && t.GetGenericTypeDefinition() = typedefof<Map<_, _>>

    override __.WriteJson (writer, value, serializer) =
      serializer.Serialize(writer, value)

    override __.ReadJson (reader, t, _, serializer) =
      let converter = 
        typedefof<mapConvert<_,_>>.MakeGenericType (t.GetGenericArguments())

      let readJson =
        converter.GetMethod("readJson")

      readJson.Invoke(null, [| reader; serializer |])
}