F# "Код недостаточно универсален" "^T не может быть обобщен" при реализации интерфейса
Я пытаюсь реализовать IJsonSerializer от Giraffe.Serialization.Json в проекте для использования Microsoft.FSharpLu.Json, но у меня возникли проблемы с одним из общих методов (код ниже)
type FSharpLuSerializer () =
interface Giraffe.Serialization.Json.IJsonSerializer with
member __.Deserialize<'T> (json : string) =
Microsoft.FSharpLu.Json.Default.deserialize<'T> json
Мне дают ошибку
Этот код не является достаточно общим. Переменная типа ^T не может быть обобщена, поскольку она выходит за пределы своей области видимости.
Я видел другие вопросы с той же ошибкой, но я не уверен, как применить их решения в моей ситуации. Я предполагаю, что это как-то связано с FSharpLu.Json, использующим ^T, но я не знаю, каким будет мой обходной путь
https://github.com/Microsoft/fsharplu/blob/master/FSharpLu.Json/Default.fs
Вот интерфейс Giraffe IJSonSerializer
[<AllowNullLiteral>]
type IJsonSerializer =
abstract member SerializeToString<'T> : 'T -> string
abstract member SerializeToBytes<'T> : 'T -> byte array
abstract member SerializeToStreamAsync<'T> : 'T -> Stream -> Task
abstract member Deserialize<'T> : string -> 'T
abstract member Deserialize<'T> : byte[] -> 'T
abstract member DeserializeAsync<'T> : Stream -> Task<'T>
3 ответа
Я столкнулся с этим, когда столкнулся с той же проблемой. Я только что написал это, и это, кажется, работает. Я прогоню его через несколько шагов, и если это будет хорошо, я выложу пакет NuGet, где его можно подключить.
open Giraffe.Serialization
open Microsoft.FSharpLu.Json
open Newtonsoft.Json
open System.IO
open System.Text
open System.Threading.Tasks
type FSharpLuJsonSerializer() =
interface IJsonSerializer with
member __.SerializeToString<'T> (x : 'T) =
Compact.serialize x
member __.SerializeToBytes<'T> (x : 'T) =
Text.Encoding.UTF8.GetBytes (Compact.serialize x)
member __.SerializeToStreamAsync<'T> (x : 'T) (stream : Stream) =
let serializer = new JsonSerializer()
serializer.Converters.Add (CompactUnionJsonConverter ())
use sw = new StreamWriter (stream)
use writer = new JsonTextWriter (sw)
serializer.Serialize (writer, obj)
Task.CompletedTask
member __.Deserialize<'T> (json : string) =
(Compact.deserialize >> box) json :?> 'T
member __.Deserialize<'T> (bytes : byte[]) =
(Compact.deserialize >> box) (Encoding.UTF8.GetString (ReadOnlySpan bytes)) :?> 'T
member __.DeserializeAsync<'T> (stream : Stream) =
Task.FromResult<'T> ((Compact.deserializeStream >> box) stream :?> 'T)
Отзывы от F# также приветствуются.
После прыжка на F# Slack, есть еще более простой способ сделать это, не требуя всего бокса и кастинга. FSharpLu использует Newtonsoft.Json под капотом, и его компактная функциональность исходит от CompactUnionJsonConverter
тип. Вы можете добавить этот конвертер к настройкам Giraffe по умолчанию JsonSerializerSettings
и это работает - это означает, что вам не нужно полностью переопределять IJsonSerializer
,
open Giraffe.Serialization
open Microsoft.FSharpLu.Json
open Newtonsoft.Json
let jsonSettings =
let x = NewtonsoftJsonSerializer.DefaultSettings
x.Converters.Add (CompactUnionJsonConverter (true))
x
Затем, где вы настраиваете свои услуги...
services.AddSingleton<IJsonSerializer>(NewtonsoftJsonSerializer jsonSettings)
Огромное спасибо Нино Флорису (@ninofloris) за это предложение.
Это беспорядочно, в общем, интерфейсы должны захватывать тип, который будет применен через обобщенные методы, чтобы еще больше усложнить ситуацию, ваша библиотека FSharpLu.Json использует встроенные SRTP, встроенный шаблон SRTP вызов метода для типа ограничения ^ поэтому в скомпилированном коде есть конкретный метод фиксированного типа для каждого вызова, он отличается от обычных обобщенных обобщенных типов, используемых в.net, которые разрешают тип во время выполнения.
Поскольку вы не можете встроить интерфейс, попробуйте добавить ограничение типа для интерфейса, которое будет перехватывать тип и избегать проблемы обобщения.
type IJsonSerializer<'T> =
abstract member SerializeToString : 'T -> string
...