首页 文章

为什么嵌套字典的反序列化会在C#中引发异常但在F#中有效?

提问于
浏览
4

我使用Newtonsoft.Json.FSharp将F#记录类型序列化为Json .

type Prices =  Dictionary<string, decimal>

type PriceList = {Id:string; Name:string; Date:DateTime; CurrencySymbol:string; Status:Status; Prices:Prices}

let private converters : JsonConverter array =
  [| BigIntConverter();
    GuidConverter();
    ListConverter();
    OptionConverter();
    MapConverter();
    TupleArrayConverter();
    UnionConverter();
    UriConverter();
    CultureInfoConverter() |]

let private settings = JsonSerializerSettings (
                        Converters = converters,
                        Formatting = Formatting.Indented,
                        NullValueHandling = NullValueHandling.Ignore)

let serialize obj = JsonConvert.SerializeObject(obj, settings)

JSON的结果是这样的:

{
  "Id": "PriceList20140201",
  "Name": "PriceList",
  "Date": "2014-02-01T00:00:00+01:00",
  "CurrencySymbol": "€",
  "Status": 0,
  "Prices": {
    "ItemCodeA": 512.4,
    "ItemCodeB": 471.0
  }
}

如果我反序列化它,它工作正常

JsonConvert.DeserializeObject<PriceList>(text)

F#Interactive中的结果:

val y : PriceList =
{Id = "PriceList20140201";
Name = "PriceList";
Date = 01.04.2014 00:00:00;
CurrencySymbol = "€";
Status = Sale;
Prices =
 dict
   [("ItemCodeA", 512.4M); ("ItemCodeB", 471.0M);...];}

现在我想使用Newtonsoft.Json在C#中反序列化它

public class PriceList
{
    public string Id { get; set; }
    public string Name { get; set; }
    public DateTime Date { get; set; }
    public string CurrencySymbol { get; set; }
    public Status Status { get; set; }
    public Prices Prices { get; set; }
}

public class Prices : Dictionary<string, decimal>
{
}
...
JsonConvert.DeserializeObject<PriceList>(json)

这会导致JsonSerializationException:

无法将当前JSON数组(例如[1,2,3])反序列化为类型'Halep.Logic.OfferManagement.Contracts.DataClasses.Pricing.Prices',因为该类型需要JSON对象(例如{“name”:“value” “})正确反序列化 . 要修复此错误,请将JSON更改为JSON对象(例如{“name”:“value”})或将反序列化类型更改为数组或实现集合接口的类型(例如ICollection,IList),例如List从JSON数组反序列化 . JsonArrayAttribute也可以添加到类型中以强制它从JSON数组反序列化 . 路径'价格',第7行,第13位 .

我读到我需要一个用于嵌套字典的CustomConverter . 但据我所知,从版本6.0开始应该可行 . 没有自定义转换器 . 我目前正在使用8.0.3 . 为什么它在F#中有效但在C#中不起作用? C#对象结构是错误的吗?

我试图直接用Dictionary替换PriceList但结果相同 .

Update 最后一句话意味着我试过这个:

public class PriceList
{
    public string Id { get; set; }
    public string Name { get; set; }
    public DateTime Date { get; set; }
    public string CurrencySymbol { get; set; }
    public Status Status { get; set; }
    public Dictionary<string, decimal> Prices { get; set; }
}

并得到几乎相同的例外:

无法将当前JSON数组(例如[1,2,3])反序列化为类型'System.Collections.Generic.Dictionary`2 [System.String,System.Decimal]',因为该类型需要一个JSON对象(例如{“名称“:”值“})正确反序列化 . 要修复此错误,请将JSON更改为JSON对象(例如{“name”:“value”})或将反序列化类型更改为数组或实现集合接口的类型(例如ICollection,IList),例如List从JSON数组反序列化 . JsonArrayAttribute也可以添加到类型中以强制它从JSON数组反序列化 . 路径'价格',第7行,第13位 .

Update 2: Solution 此代码有效 . @JustinNiessner的最后评论导致了解决方案 . 问题是输入实际上是包含[]而不是{}的输入:

{
  "Id": "PriceList20140201",
  "Name": "PriceList",
  "Date": "2014-02-01T00:00:00+01:00",
  "CurrencySymbol": "€",
  "Status": 0,
  "Prices": [
    "ItemCodeA": 512.4,
    "ItemCodeB": 471.0
  ]

}

1 回答

  • 5

    type Prices = Dictionary<string, decimal> 是F#中的类型缩写,而不是像C#代码那样创建一个继承自 Dictionary<string, decimal> 的新类:

    F# Type Abbreviations

    这就是为什么你会看到两者之间的反序列化差异 .

    有两种方法可以在C#中解决这个问题 . 第一个是使用 using 指令创建一个类似的类型别名:

    using Prices = System.Collections.Generic.Dictionary<string, object>;
    

    另一种方法是在 PriceList 类定义中简单地用 Decimal<string, decimal> 替换类型:

    public class PriceList
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public DateTime Date { get; set; }
        public string CurrencySymbol { get; set; }
        public Status Status { get; set; }
        public Dictionary<string, decimal> Prices { get; set; }
    }
    

    然后C#Desserialization的工作方式与F#相同 .

相关问题