如何在F#中定义SingleOrArrayConverter处理JSON属性(C#移植)
解决F#中移植C# SingleOrArrayConverter JSON转换器的问题
问题分析
你的F#代码存在几个关键问题导致转换器无法正常工作:
- ReadJson返回类型不匹配:C#版本返回
List<T>,但你的F#代码在非数组分支返回了数组'T[],类型不兼容会引发反序列化失败。 - 模型类型定义不一致:
Locality的字段类型和C#原定义不符(比如postcode在C#是string,你写成了int;latitude/longitude是可空double,你写成了不可空decimal)。 - 属性装饰器未正确应用:
Localities的locality字段缺少必要的JsonProperty和JsonConverter特性。
修正后的完整代码
1. 正确实现SingleOrArrayConverter
open Newtonsoft.Json open Newtonsoft.Json.Linq open System type SingleOrArrayConverter<'T>() = inherit JsonConverter() override this.CanConvert(objectType: Type) = objectType = typeof<List<'T>> override this.ReadJson(reader: JsonReader, objectType: Type, existingValue: obj, serializer: JsonSerializer) = let token = JToken.Load(reader) if token.Type = JTokenType.Array then token.ToObject<List<'T>>() :> obj else let singleItem = token.ToObject<'T>() List.singleton singleItem :> obj override this.CanWrite with get() = false override this.WriteJson(writer: JsonWriter, value: obj, serializer: JsonSerializer) = raise (NotImplementedException())
2. 修正后的模型定义
open Newtonsoft.Json type Locality = { category: string id: int location: string postcode: string // 匹配C#的string类型 state: string latitude: double option // 用F# option类型表示可空值 longitude: double option } type Localities = { [<JsonProperty("locality"); JsonConverter(typeof<SingleOrArrayConverter<Locality>>)>] locality: List<Locality> } type PostCodeLocality = { localities : Localities }
关键修改说明
- ReadJson返回值:无论分支如何,都返回
List<'T>并向上转型为obj(符合基类方法的返回类型要求),F#里用List.singleton创建单元素列表,和C#的new List<T> { ... }等价。 - 可空类型处理:F#中没有C#的
Nullable<T>语法糖,用option类型表示可空值(double option对应C#的double?)。 - 属性装饰器:在
Localities的locality字段上正确添加JsonProperty(指定JSON字段名)和JsonConverter(绑定自定义转换器),F#的特性语法用[<特性1; 特性2>]的形式。 - 去掉不必要的mutable:
JToken.Load(reader)不需要用mutable声明,直接用不可变绑定即可。
内容的提问来源于stack exchange,提问作者Martin Thompson
相关产品推荐
相关产品推荐

