fsprojects / fleece Goto Github PK
View Code? Open in Web Editor NEWJson mapper for F#
Home Page: http://fsprojects.github.io/Fleece
License: Apache License 2.0
Json mapper for F#
Home Page: http://fsprojects.github.io/Fleece
License: Apache License 2.0
Until now, Fleece for FSharp.Data defined JsonObject as a (string * JsonValue) []
.
This implies that depending on how do we interpret it, could be serialized either as JObject
or JArray
which might result in problems.
The idea is to use a type alias to a type that already serialize to JObject
.
I propose to use Map<string,JsonValue>
.
We could consider IReadOnlyDictionary<string,JsonValue>
, which is used internally and we don't have an overload for it, but note that being an interface, we need to assess first that no undesired subsumption would happen in the overload resolution.
Note that this might break things in projects using FSharp.Data
, but for System.Text.Json
there won't be problems as long as this change is included in its first release.
Finally, I don't know why the type alias to (string * JsonValue) []
was choosen, not sure if @eulerfx can remember the reason and give some feedback.
I have the following type with JsonObjCodec defined:
type User = {
Id: UserId
DateOfBirth: DateTime
Country: string
State: string
SexAtBirth: SexMeasure
EthnicBackground: string
Education: string
Occupation: string
Nationality: string
SexualOrientation: string
RelationshipStatus: string
Children: int
Phone: int
Email: string
RegistrationCompleted: bool
Age: AgeMeasure
Weight: BiometricMeasure
Height: BiometricMeasure
LastMod: UTCTick
RoadmapStatus: RoadmapStatus
LastProcessedGuidelineId: int
QuestionsStore: QuestionStore list
MedicalConditions: MedicalConditionStore list
RejectedMedicalConditions: RejectedMedicalConditionStore list
Scores: ScoreStore list
Biometrics: BiometricStore list
} with
static member JsonObjCodec =
(fun id db co st se eb educ occup nat sexo rels chi pho ema regc ag we he lm rs lid qs mc rmc sc bi ->
{Id = id; DateOfBirth = db; Country = co; State = st; SexAtBirth = se; EthnicBackground = eb;
Education = educ; Occupation = occup; Nationality = nat; SexualOrientation = sexo;
RelationshipStatus = rels; Children = chi; Phone = pho; Email = ema
RegistrationCompleted = regc; Age = getAge(db); Weight = we; Height = he;
LastMod = lm; RoadmapStatus = rs; LastProcessedGuidelineId = lid; QuestionsStore = qs;
MedicalConditions = mc; RejectedMedicalConditions = rmc; Scores = sc; Biometrics = bi })
<!> jreq "Id" (Some << fun x -> x.Id)
<*> jreq "DateOfBirth" (Some << fun x -> x.DateOfBirth)
<*> jreq "Country" (Some << fun x -> x.Country)
<*> jreq "State" (Some << fun x -> x.State)
<*> jreq "SexAtBirth" (Some << fun x -> x.SexAtBirth)
<*> jreq "EthnicBackground" (Some << fun x -> x.EthnicBackground)
<*> jreq "Education" (Some << fun x -> x.Education)
<*> jreq "Occupation" (Some << fun x -> x.Occupation)
<*> jreq "Nationality" (Some << fun x -> x.Nationality)
<*> jreq "SexualOrientation" (Some << fun x -> x.SexualOrientation)
<*> jreq "RelationshipStatus" (Some << fun x -> x.RelationshipStatus)
<*> jreq "Children" (Some << fun x -> x.Children)
<*> jreq "Phone" (Some << fun x -> x.Phone)
<*> jreq "Email" (Some << fun x -> x.Email)
<*> jreq "RegistrationCompleted" (Some << fun x -> x.RegistrationCompleted)
<*> jreq "Age" (Some << fun x -> getAge x.DateOfBirth)
<*> jreq "Weight" (Some << fun x -> x.Weight)
<*> jreq "Height" (Some << fun x -> x.Height)
<*> jreq "LastMod" (Some << fun x -> x.LastMod)
<*> jreq "RoadmapStatus" (Some << fun x -> x.RoadmapStatus)
<*> jreq "LastProcessedGuidelineId" (Some << fun x -> x.LastProcessedGuidelineId)
<*> jreq "QuestionsStore" (Some << fun x -> x.QuestionsStore)
<*> jreq "MedicalConditions" (Some << fun x -> x.MedicalConditions)
<*> jreq "RejectedMedicalConditions" (Some << fun x -> x.RejectedMedicalConditions)
<*> jreq "Scores" (Some << fun x -> x.Scores)
<*> jreq "Biometrics" (Some << fun x -> x.Biometrics)
I would like to compute "Age" based on "DateOfBirth":
<*> jreq "Age" (Some << fun x -> getAge x.DateOfBirth)
That means that the front-end should be able to send a JSON payload without "Age" in it, as age will always be computed on the backend based on "DateOfBirth". But this does not work, as when I send a JSON without "Age", I get the error that there is no match for "Age":
System.Exception: Property: 'Age' not found in object 'Microsoft.FSharp.Core.ExtraTopLevelOperators+DictImpl`3[Microsoft.FSharp.Core.CompilerServices....
System.Exception
Property: 'Age' not found in object 'Microsoft.FSharp.Core.ExtraTopLevelOperators+DictImpl`3[Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers+StructBox`1[System.String],System.String,Newtonsoft.Json.Linq.JToken]'
at [email protected](String json)
Any ideas on how to achieve my goal? Thanks!
Using Fleece together with F#+ can cause some weirdness, mainly due to Failure being both defined in F#+ and Fleece.
Internally in Fleece this is JsonValue
, but externally this is not always re-exported.
Only for Newtonsoft it is defined as JsonValue
, other implementations needs to open their specific namespace (ie: open FSharp.Data) in order to have JsonValue
available.
This is normally not a big issue, but it would be nice (and possible clearer) if Fleece expose a unique type alias that maps to the specific JsonValue (or JToken) of the underlying Json lib.
It could be called JsonValue
or JValue
.
Trying to use a package where Fleece.NewtonsoftJson
is a transient dependency. My paket's source is v3: https://api.nuget.org/v3/index.json
. When issuing paket install
, got error:
Conflict detected:
- Fleece.NewtonsoftJson 0.7.0 requested package FSharpPlus: >= 1.1.0-CI00245 (CI)
- Available versions:
- (1.1.0-ci00252, [https://api.nuget.org/v3/index.json])
- (1.1.0-ci00245, [https://api.nuget.org/v3/index.json])
...
Notice different in caps casing of CI.
Change paket's source to https://www.nuget.org/api/v2
and paket install
works fine.
Expected behavior: Fleece.NewtonsoftJson
should resolve vor v2 and v3 api version of nuget.
I'd like to migrate my project to .NET Core.
It appears that Fleece is the showstopper. Do you have plans on becoming .NET Standard compatible?
Thanks,
Jedrek
Depends on fsprojects/FSharp.Data#1182
Fleece.fs#L117-L118 (Newtonsoft.Json):
| JTokenType.Integer -> JNumber o
| JTokenType.Float -> JNumber o
Where o is JsonValue
Fleece.fs#L223-L224 (FSharp.Data):
| JsonValue.Number _ as x -> JNumber x
| JsonValue.Float _ as x -> JNumber x
Where x is JsonValue
While for System Json there doesn't seem to be such a distinction.
The function JNumber
takes a decimal as parameter. It would make sense to be able to use float as well.
Trying to deserialize a null value into an option produces an obj expected value (json type mismatch). The behavior should be consistent whether the value is null or doesn't exist, but that's not the case. It correctly returns a None
when the value is absent, and and error when the value is present, but set to null.
Repro
type Address = {
country: string }
with
static member OfJson json =
match json with
| JObject o -> monad {
let! country = o .@ "country"
return { country = country }}
| x -> Decode.Fail.objExpected x
type Customer = {
id: int
name: string
address: Address option}
with
static member OfJson json =
match json with
| JObject o -> monad {
let! id = o .@ "id"
let! name = o .@ "name"
let! address = o .@? "address"
return {
id = id
name = name
address = address } }
| x -> Decode.Fail.objExpected x
Succeds - Returns Some { id = 1; name = "Joe"; address = None}
let json = """{"id":1, "name": "Joe"}"""
let (customer:Customer ParseResult) = parseJson json
Fails - Should return Some { id = 1; name = "Joe"; address = None}
let json' = """{"id":1, "name": "Joe", "address": null}"""
let (customer':Customer ParseResult) = parseJson json
a potential solution for this issue would be to modify jgetOptionWith
function like:
let inline jgetOptWith ofJson (o: IReadOnlyDictionary<string, JsonValue>) key =
match o.TryGetValue key with
| true, JNull -> Success None
| true, value -> ofJson value |> map Some
| _ -> Success None
//edited to update the test cases and confirm the solution works for other libraries as well.
So I find myself writing the following quite a bit:
type VariableName = VariableName of string
module VariableName =
let value (VariableName x) = x
module VariableNameCodec =
let ofDomain = VariableName.value
let toDomain = VariableName
let encoder = ofDomain >> JsonEncode.string
let decoder = JsonDecode.string >> Result.map toDomain
let codec = decoder, encoder
And then I saw Codec.invmap
and assumed that would satisfy me and I could write JsonCodec.string |> Codec.invmap toDomain ofDomain
. However invmap
works in the other direction, mapping the JsonValue part of the codec rather than the VariableName part of the codec. Or alternatively, the input to the decoder and the output of the encoder.
I think it would be nice to add the following alongside invmap
.
let inline contrainvmap f g (decoder, encoder) =
map (map f) decoder, contramap g encoder
Although I'm not sure the name is quite correct because actually on the decoder it's mapping twice (once through the function and then through the result type it returns) and also because I'm not sure if "contrainvmap" is the correct category theory based name for this thing.
Thoughts on adding this (assuming I've not overlooked something that already exists which does this)?
It would make sense to have Fleece System.Json as a separate delivery with a system json inspired namespace in order to promote that it's not the default but rather an implementation among others.
Using the main namespace for System.Json makes it difficult to create a future common delivery Fleece
with shared parts.
Hi @mausch,
I just discovered Fleece and really appreciate it.
My problem is I have a File so a string representation of Json and I would like to instantiate a type from it.
Here is the code:
type ConfigJson = {
RelativeRoot: string
}
type ConfigJson with
static member Create relativeRoot = { ConfigJson.RelativeRoot = relativeRoot }
static member FromJson(_:ConfigJson) =
function
| JObject o -> ConfigJson.Create <!> (o .@ "relativeRoot")
| x -> Failure (sprintf "Expected ConfigJson, found %A" x)
type Config private () =
let mutable RelativeRoot = ""
let mutable json = nullArg
do
let data = parseJSON(System.IO.File.ReadAllText("server.json"))
(*ConfigJson.FromJson(data)*)(* Here is the problem *)
|> ignore
static let instance = Config()
I was supposing that FromJson was a method to instantiate the Type but I can't find how to use it. Sure it doesn't take a string as a entry. Could you help me please.
PS: Sorry, if the question is a little dumb.
Fleece has no overload for ArraySegment<'a> on jfieldOption, we could edit the generated code in Falanx to emit array 'a
or add an overload to fleece for ArraySegment<'a>.
See jet/falanx#80
Title says it all ;)
Currently all dlls compile to the same namespace: Fleece
.
This means that it would be impossible to use two implementations at the same time (in the same project).
I propose to use different namespaces:
Fleece.FSharpData
Fleece.NewtonSoft
Fleece // I would keep it for System.Json for now
Not sure if this issue belongs in Fleece or FsControl, but thought I'd let you know about it anyway. :)
gusty/FsControl#43
If I update the fsproj (dtchepak@8808caa) to build with F# 4 I get the following error when trying to build:
>c:\dev\git\fleece\Fleece\unknown(1,1): warning FS3186: An error occurred while reading the F# metadata node at positio
n 878 in table 'ivals' of assembly 'FsControl.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. The node h
ad no matching declaration. Please report this warning. You may need to recompile the F# assembly you are using. [c:\d
ev\git\fleece\Fleece\Fleece.fsproj]
So, context: I've been trying to help get the build passing on Linux so I can help with documentation updates. In doing this, I've noticed: there are ergonomics challenges with requiring local-path release DLLs at the top of the script -- like this, taken from codec.fsx
:
#r @"../../src/Fleece.SystemJson/bin/Release/netstandard2.1/System.Json.dll"
#r @"../../src/Fleece.SystemJson/bin/Release/netstandard2.1/Fleece.SystemJson.dll"
#r @"../../src/Fleece.SystemJson/bin/Release/netstandard2.1/FSharpPlus.dll"
The challenge here is, any change to the target framework for a library requires updating all the doc scripts, and the scripts themselves can't be loaded by an IDE until a release build is run. This is not insurmountable. And, I haven't actually worked with the F# doc system in use here before; this might be for the best!
This all said: I'd like to suggest the docs instead pull deps from Nuget using the paket, more like:
#r "paket:
nuget System.Json 4.7.1
nuget Fleece.SystemJson 0.9.0
nuget FsharpPlus"
Versions could be pinned or not (I'd have to confirm behaviour, but I assume absent a pin, latest is pulled, which seems reasonable to me).
Thoughs?
let result : int ParseResult = parseJSON "1.1"
let (Choice1Of2 value) = result
//val value : int = 1
I feel this is incorrect and should instead result in a failure.
There's possibly other similar edge cases like this one.
On Linux, trying to build Fleece without mono development libraries results in errors:
/var/home/gastove/Code/open-source/Fleece/src/Fleece.FSharpData/unknown(1,1): error FS0078: Unable to find the file 'mscorlib.dll' in any of \Reference Assemblies\Microsoft\Framework\.NETFramework /var/home/gastove/Code/open-source/Fleece/src/Fleece.FSharpData /usr/share/dotnet/sdk/3.1.405/FSharp/ [/var/home/gastove/Code/open-source/Fleece/src/Fleece.FSharpData/Fleece.FSharpData.fsproj]
/var/home/gastove/Code/open-source/Fleece/src/Fleece.NewtonsoftJson/unknown(1,1): error FS0078: Unable to find the file 'mscorlib.dll' in any of \Reference Assemblies\Microsoft\Framework\.NETFramework /var/home/gastove/Code/open-source/Fleece/src/Fleece.NewtonsoftJson /usr/share/dotnet/sdk/3.1.405/FSharp/ [/var/home/gastove/Code/open-source/Fleece/src/Fleece.NewtonsoftJson/Fleece.NewtonsoftJson.fsproj]
/var/home/gastove/Code/open-source/Fleece/src/Fleece.SystemJson/unknown(1,1): error FS0078: Unable to find the file 'mscorlib.dll' in any of \Reference Assemblies\Microsoft\Framework\.NETFramework /var/home/gastove/Code/open-source/Fleece/src/Fleece.SystemJson /usr/share/dotnet/sdk/3.1.405/FSharp/ [/var/home/gastove/Code/open-source/Fleece/src/Fleece.SystemJson/Fleece.SystemJson.fsproj]
/var/home/gastove/Code/open-source/Fleece/src/Fleece.SystemTextJson/unknown(1,1): error FS0078: Unable to find the file 'mscorlib.dll' in any of \Reference Assemblies\Microsoft\Framework\.NETFramework /var/home/gastove/Code/open-source/Fleece/src/Fleece.SystemTextJson /usr/share/dotnet/sdk/3.1.405/FSharp/ [/var/home/gastove/Code/open-source/Fleece/src/Fleece.SystemTextJson/Fleece.SystemTextJson.fsproj]
46 Warning(s)
4 Error(s)
This can be fixed two different ways (that I know of so far):
net472
(Fleece specifies 461
in many places).It's also possible this could be fixed through msbuild
magic, though I can't claim to know how to do that myself.
We can try adding an overload for seq<'t>
, the first question to answer is whether it makes sense.
I think it could be problematic if it accepts all IEnumerable<'t>
subclasses, for instance it will fail the roundtrip, since it will convert back to an IEnumerable<'t>
of whichever underlying implementation we decide as default.
If we manage to make it strict to seq<'t>
we just need to find out what's the best underlying implementation to use as default (an array, a list, a json reader), then the roundtrip will succeed in terms of types, it would just change the underlying representation which I don't think is really an issue, since the contract is about types.
The current implementation uses the following lib:
https://github.com/mausch/ReadOnlyCollections
Is this needed now that read only collections are available in all supported versions of .net?
I think right now the only possibility to parse/encode pairs is through Dictionaries and Objects.
A KeyValuePair
would allow to have full control over property/value in cases where we want something else, ie: a MultiMap, a Map where the key is not a string (though we can work it out in another issue) or simply a tuple that it's not encoded as a linear array of 2 elements.
I have the following DU which is composed of other DUs or/and Records.
type BiometricRules =
| Age of Comparator * AgeMeasure
| Glycemia of Comparator * BiometricMeasure
| Biometric of BiometricType * Comparator * BiometricMeasure
| Sex of SexMeasure
| MedicalCondition of MedicalCondition
| Score of ScoreType * Comparator * ScoreMeasure
While trying to deserialize and serialize with Fleece, I have written the following JsonObjCodec
.
For an unknown reason, it does not compile with the error No overloads match for method 'Map'. All the nested DUs or Records have either a JsonObjCodec or static FromString and ToString methods defined.
Any solution with respect to how I could solve this via Fleece would be appreciated. The library is already deeply used in the project, so changing it would involve too much refactoring.
Below I copy-pasted the definition of the other DU and Records, as reference:
type Comparator =
| GreaterThan
| LowerThan
| LowerThanOrEqual
| GreaterThanOrEqual
| EqualTo
with
override this.ToString() =
match this with
| GreaterThan -> ">"
| LowerThan -> "<"
| LowerThanOrEqual -> "<="
| GreaterThanOrEqual -> ">="
| EqualTo -> "="
static member FromString s =
match s with
| ">" -> GreaterThan
| "<" -> LowerThan
| ">=" -> GreaterThanOrEqual
| "<=" -> LowerThanOrEqual
| "=" -> EqualTo
| _ -> failwith "Not a valid comparator."
type AgeMeasure =
| Years of decimal
| Months of decimal
| Weeks of decimal
with
override this.ToString() =
match this with
| Years y -> string y + " years"
| Months m -> string m + " months"
| Weeks w -> string w + " weeks"
static member FromString (s: string) =
match s with
| _ when s.EndsWith("years") -> Years (Decimal.Parse(s.Replace("years", "")))
| _ when s.EndsWith("months") -> Months (Decimal.Parse(s.Replace("months", "")))
| _ when s.EndsWith("weeks") -> Weeks (Decimal.Parse(s.Replace("weeks", "")))
type BiometricMeasure = {
Value: decimal
UoM: string option
} with
static member JsonObjCodec =
fun va uom -> {
Value = va
UoM = if uom = "NA" then None else Some uom
}
<!> jreq "Value" (Some << fun bm -> bm.Value)
<*> jreq "UoM" (Some << fun bm -> if bm.UoM |> Option.isNone then "NA" else bm.UoM |> Option.get)
type BiometricType =
| SBP
| DBP
| Glycemia
| Specified of string
with
override this.ToString() =
match this with
| SBP -> "SBP"
| DBP -> "DBP"
| Glycemia -> "Glycemia"
| Specified s -> s
static member FromString s =
match s with
| "SBP" -> SBP
| "DBP" -> DBP
| "Glycemia" -> Glycemia
| _ -> Specified s
type SexMeasure =
| Female
| Male
| Other of string
with
override this.ToString() =
match this with
| Female -> "Female"
| Male -> "Male"
| Other s -> s
static member FromString (s: string) =
match s.ToLower() with
| "Female" -> Female
| "Male" -> Male
| other -> Other other
type MedicalCondition =
| ICD of ICD
| Other of string
with
static member JsonObjCodec =
ICD <!> jreq "MedicalCondition" (function ICD v -> Some v | _ -> None)
<|> (Other <!> jreq "MedicalCondition" (function Other v -> Some v | _ -> None))
type ScoreType =
| BMI
| Other of string
with
override this.ToString() =
match this with
| BMI -> "BMI"
| Other s -> s
static member FromString s =
match s with
| "BMI" -> BMI
| _ -> Other s
type ScoreMeasure = decimal
Libraries Used in project:
<PackageReference Update="FSharp.Core" Version="4.7" />
<PackageReference Include="FSharpPlus" Version="1.1.1" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="Fleece.NewtonsoftJson" Version="0.8.0" />
<PackageReference Include="FSharp.Data" Version="3.3.3" />
Also posted on StackOverflow
It would be great to have Fleece for JsonValue. A port is here.
As an aside, I've been on the fence about taking a dependency on FsControl/FSharpPlus. In this case, only traverse
and fmap
were needed and taking that dependency seemed overkill. What has your experience been?
I think this project started with original Aeson names, which at the same time are Haskell-ish names.
I propose we use instead more F#ish names, which means that instead of fromJSON
we should use ofJSON
.
This can be implemented without breaking changes, by adding another default overload, or by renaming the recently added default for FromJSON
with a clean signature, since it wasn't released yet.
See for instance the function proposed here
The idea is that we can start chaining errors, so we know exactly where the issue is.
Right now, for json objects we can detect that a property is not found, but what if the property exists but say, has the wrong value?
The user gets an EncodingCaseMismatch
but he doesn't know where it happened, I propose we introduce something like:
ErrorwhileDecoding of property: string * inner: DecodeError
We're planning to continue supporting this library !
I think the first step will be to upgrade the solution:
When serializing to and deserializing from JSON, I've often found it convenient to forgo serialization/deserialization for certain fields like so:
type Foo =
{ Foo : string array
; Bar : JsonObject
}
While writing the OfJson
member is straightforward:
static member inline OfJson json =
match json with
| JObject o ->
(fun f b -> { Foo = f; bar = b})
<!> (o .@ "foo")
<*> (o .@ "bar")
| x -> Decode.Fail.objExpected x
Writing the ToJson
member is impossible AFAIK as JsonObject
does not have a ToJson
static member. Is it possible to add this to Fleece?
Additionally, are there any reasons that JsonValue
doesn't have ToJson
and OfJson
instances? Will a PR adding these be welcomed?
Hi @mausch,
Not sure if this is a silly idea, but was wondering if it is possible to drop the FsControl and FSharpPlus dependencies from Fleece? Beyond map/apply/traverse for Choice<'a,'b>
I'm not sure where else they are used?
I'm happy to try a PR if this sounds reasonable.
is there any way to get current url by using your fleece or any other option
Is there a way to pass parameters to OfJson
or ToJson
properties?
I would like to conditionally switch between different versions of underlying JSON model based on a parameter for example revision
, specially for DU types, where revisions are not part of the main type being serialized/deserialized.
Currently jreqWith
etc require that the codec passed to them is a "pair of functions" type codec. Such as (JsonValue -> a' ParseResult) * (a' -> JsonValue)
. However, when defining codecs for more complex types it's preferable to use the applicative style which creates a ConcreteCodec
. It's then cumbersome to use this codec in another codec as it requires converting back to "pair of functions" type by doing codec |> Codec.ofConcrete |> Codec.compose jsonObjToValueCodec
.
As an example consider the following types:
type Foo = { X: int }
type Bar = { Foo: Foo }
Then you might want to define codecs for them in another module which currently requires writing them like this:
module Foo =
let codec =
fun x -> { X = x }
<!> jreq "x" (fun x -> x.X)
module Bar =
let codec =
fun foo -> { Foo = foo }
<!> jreqWith (Foo.codec |> Codec.ofConcrete |> Codec.compose jsonObjToValueCodec) "foo" (fun x -> x.Foo)
So it would be nicer if we could avoid having to convert between the two codec representations.
The same way we have jfieldOpt
which interpret the absence of a field as a None, we could add a combinator that interpret the absence as an empty array.
How can we call that combinator?
Or maybe we can upgrade the existing jfieldOpt
to handle multiple values.
Hello, Is there any example of using Fleece with a generic type? Below is the code I'm working on, but I can't figure out the type constraitns of 'a.
type Maybe<'a> = Some of 'a // what should the type constraint of 'a?
with static member ToJson(x: Maybe<'a>) =
match x with
| Some(v) -> jobj ["some" .= v ]
type Data = Data of string
with
static member ToJson(x: Data) =
let (Data id) = x
JString id
static member OfJson json =
match json with
| JString id -> Some id |> Decode.Success
| x -> Error <| Uncategorized (sprintf "failed to decode: %A" x)
Would be very useful to have unit tests (i see the project is empty) and/or documentation to work with FSharp.Data.JsonValue lenses
In theory, this library could be perfectly used in the Fable ecosystem.
Although it allows heavy use of SRTP, it also permits to manually specify a codec combinator, the same way ELM inspired Json libs do.
On top of that, the feature of specifying a single codec would also work without relying on SRTP overload, by specifying a codec for each field.
The last sample code in the readme https://github.com/mausch/Fleece#combinators illustrate this.
There shouldn't be any issue to use this in Fable.
I'm not sure what is required in order to by Fable ready, all I know is that there are no stoppers.
Is there anyone interested in take it there?
When using a net471 f# project which references Fleece and System.Json (4.5.0), msbuild gives a warning that it cant resolve conflicts between the two.
Are there any plans to update System.Json to use the latest version (4.5.0) ?
Roundtripping a string option
doesn't work. Problem is, serializing Some null
outputs null
which then gets deserialized as None
. This problem can be generalized to all reference types within an option.
This doesn't happen in Haskell/Aeson because strings are not nullable.
It seems there are (at least) a couple of ways that people encode DUs in JSON.
The first way, which is currently documented, uses the case name as the property name which then encloses that case's data. For example the following F# type
type Foo = { X: int }
type Bar = { Y: string }
type Baz =
| Foo of Foo
| Bar of Bar
The Foo
case would be encoded as
{
"foo": {
"x": 1
}
}
And the Bar
case would be encoded as:
{
"bar": {
"y": "string"
}
}
I think this way makes a lot of sense when you want to round trip to a DU on both sides.
A second way, which isn't documented, that is common if you're using TypeScript on the client is to enclose the case label under some "tag" prop. This is even more relevant I think if you have some DU cases that don't have any data. For example:
type Foo = { X: int }
type Baz =
| Foo of Foo
| Bar
Then the Foo
case would be encoded as:
{
"type": "foo",
"x": 1
}
And the Bar
case would be:
{
"type": "bar"
}
Where obviously the "type" field could be named anything.
I think this second case needs documenting, along with any other ways of commonly encoding DUs, as it's not immediately obvious how to build a codec for this second type.
I think the way you want to go about writing the codec for this second way is to start with codecs for the data in each of the case labels and then apply a combinator that adds a tag prop for the relevant case and (maybe a second combinator) that lifts the codec into the DU type by applying the case constructor when decoding and unwrapping the value when encoding. You then want to use an alternative to bring all of those codecs together.
I'm happy to open a PR to improve the docs to add an example for the second case.
I was wondering, since F#+ is anyway a dependency of this project, is there any drawback in adding F#+ Data types like NonEmptyList and so on, to the overload list ?
At the moment I can't think of any downside.
See the following issue in Falanx
jet/falanx#131
open System.Json
open Fleece.SystemJson
open Fleece.SystemJson.Operators
type PersonF = {
name : string * string
age : int option
children: PersonF list }
with
static member JsonObjCodec =
fun f l a c -> { name = (f, l); age = a; children = c }
|> withFields
|> jfield "firstName" (fun x -> fst x.name)
|> jfield "lastName" (fun x -> snd x.name)
|> jfieldOpt "age" (fun x -> x.age)
|> jfield "children" (fun x -> x.children)
Should we switch to Result<_,_>
and discard Choice<_,_>
?
This will not be a source breaking change since we use an alias, but it will be a binary breaking change, anyway we're still in 0.x so it should be fine.
How about people who's using it in production? @BlythMeister @harrisonmeister what's your feeling about it?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.