![](/img/trans.png)
[英]F# Data JSON type provider: How to handle a JSON property that can be an array or a property?
[英]How to define SingleOrArrayConverter to Handle JSON property in F# ( Port from C# )
我正在将一些(随着时间的推移经过良好测试)代码从 C# 移植到 F# 并且在 F# 中遇到一些问题
C# 代码:
(Object 我要序列化)
public class Locality
{
public string category { get; set; }
public int id { get; set; }
public string location { get; set; }
public string postcode { get; set; }
public string state { get; set; }
public double? latitude { get; set; }
public double? longitude { get; set; }
}
public class Localities
{
[JsonProperty("locality")]
[JsonConverter(typeof(R2H.Models.JSon.SingleOrArrayConverter<Locality>))]
public List<Locality> locality { get; set; }
}
public class AuspostPostCodeLocality
{
public Localities localities { get; set; }
}
(JSON转换器)
public class SingleOrArrayConverter<T> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(List<T>));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JToken token = JToken.Load(reader);
if (token.Type == JTokenType.Array)
{
return token.ToObject<List<T>>();
}
return new List<T> { token.ToObject<T>() };
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
(尝试 F# 代码)
type SingleOrArrayConverter<'T>() =
inherit JsonConverter()
override this.CanConvert(objectType : Type) =
objectType = typeof<List<'T>>
override this.ReadJson(reader : JsonReader, objectType : Type, existingValue : System.Object, serializer : JsonSerializer) =
let mutable (token : JToken) = JToken.Load (reader)
if token.Type = JTokenType.Array then
(token.ToObject<List<'T>> ())
else
([|(token.ToObject<'T> ())|])
override this.CanWrite
with get() =
false
override this.WriteJson(writer : JsonWriter, value : System.Object, serializer : JsonSerializer) =
raise (new NotImplementedException() :> System.Exception)
还有我在 model 上的尝试(你可以看到几个尝试被注释掉了)。
type Locality = {
category: string
id: int
location: string
postcode: int
state: string
latitude: decimal
longitude: decimal
}
type Localities = {
//inherit JsonConverter<SingleOrArrayConverter<Locality>>()
//[<JsonProperty("locality");JsonConverter<SingleOrArrayConverter<Locality>>>]
//[<JsonConverter(typeof (SingleOrArrayConverter<Locality>))>]
//[<JsonProperty("locality")>]
locality: List<Locality>
}
type PostCodeLocality = {
localities : Localities
}
由于您的转换器似乎源于Brian Rogers对How to handle a single item and an array for the same property using JSON.net 的回答,我假设您正在尝试为System.Collections.Generic.List<'T>
类型的可变列表创建通用转换器System.Collections.Generic.List<'T>
(在FSharpx.Collections
ResizeArray
。
F# 也有一个不可变的列表类型FSharp.Collections.List<'T>
缩写为list
。 确定您要使用哪一个。 [1]
考虑到这一点,假设您想要System.Collections.Generic.List<'T>
您的转换器可以编写如下:
type SingleOrArrayConverter<'T>() =
inherit JsonConverter()
override this.CanConvert(objectType) = objectType = typeof<ResizeArray<'T>>
override this.ReadJson(reader, objectType, existingValue, serializer) = // Unlike in C# it's not necessary to declare the types of the arguments when there is no ambiguity
let token = JToken.Load (reader)
if token.Type = JTokenType.Array then
// Upcast to obj as upcasting is not automatic for returned value
// https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/casting-and-conversions#upcasting
(token.ToObject<ResizeArray<'T>> ()) :> obj
else
ResizeArray [|(token.ToObject<'T> ())|] :> obj // Convert array to List<T> then upcast to object
override this.CanWrite = false // Simplified syntax
override this.WriteJson(writer, value, serializer) = raise (new NotImplementedException())
并且您的Localities
类型定义如下:
type Localities = {
[<JsonProperty("locality")>]
[<JsonConverter(typeof<SingleOrArrayConverter<Locality>>)>] // Fix syntax: typeof<'T> not typeof(T)
locality: ResizeArray<Locality> // Be sure whether you want System.Collections.Generic.List<'T> a.k.a ResizeArray<'T> or FSharp.Collections.List<'T>
}
演示小提琴#1在这里。
如果您确实想使用 f# 的不可变列表,请按如下方式定义您的转换器:
type SingleOrArrayFSharpListConverter<'T>() =
inherit JsonConverter()
override this.CanConvert(objectType) = objectType = typeof<list<'T>>
override this.ReadJson(reader, objectType, existingValue, serializer) = // Unlike in C# it's not necessary to declare the types of the arguments when there is no ambiguity
let token = JToken.Load (reader)
if token.Type = JTokenType.Array then
// Upcast to obj as upcasting is not automatic for returned value
// https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/casting-and-conversions#upcasting
(token.ToObject<list<'T>> ()) :> obj
else
[token.ToObject<'T>()] :> obj // Convert array to list<T> then upcast to object
override this.CanWrite = false // Simplified syntax
override this.WriteJson(writer, value, serializer) = raise (new NotImplementedException())
并修改Localities
如下:
type Localities = {
[<JsonProperty("locality")>]
[<JsonConverter(typeof<SingleOrArrayFSharpListConverter<Locality>>)>] // Fix syntax: typeof<'T> not typeof(T)
locality: Locality list // Be sure whether you want System.Collections.Generic.List<'T> a.k.a ResizeArray<'T> or FSharp.Collections.List<'T>
}
演示小提琴#2在这里。
[1]有关详细信息,请参阅F# 中的创建通用列表 <T> 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.