繁体   English   中英

如何定义 SingleOrArrayConverter 以处理 F# 中的 JSON 属性(来自 C# 的端口)

[英]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 RogersHow 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM