Giter Club home page Giter Club logo

fleece's People

Contributors

7sharp9 avatar blythmeister avatar dtchepak avatar enricosada avatar eulerfx avatar gastove avatar gusty avatar mausch avatar mt-caret avatar pnobre avatar wallymathieu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fleece's Issues

Use a different type alias for JsonObject

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.

Compute Field in jsonObjCodec based on another field

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!

F#+ and Fleece ParseResult

Using Fleece together with F#+ can cause some weirdness, mainly due to Failure being both defined in F#+ and Fleece.

Expose a type alias of the underlying JsonValue representation

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.

Nuget api v3 can not resolve Fleece.NewtonsoftJson

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.

JNumber

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.

Deserializing null values to Option

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.

Opposite of Codec.invmap would be useful

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)?

[Proposal] Separate delivery for Fleece System.Json

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.

Question : How to parse a JSON string

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.

Individual namespaces for each Json distribution

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

Problem building with F#4.0 - FsControl

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]

Proposal/Quesion: add `paket` to docs, use fsx paket extension

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?

Build on Linux Requires Mono; Sort out What to Do About That

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):

  1. Document the Mono requirement and be at peace with it.
  2. Update the target frameworks for Fleece. In the F# slack, it's suggested that target framework needs to be greater than 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.

Overload for seq<'t>

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.

Add overload for KeyValuePair

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.

Ease way to add JsonObjCodec for types with string transformations

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

Provide support for JsonValue in FSharp.Data

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?

Move to FsProjects

We would like to move this library to FsProjects.

@forki can you do the move? @mausch will do whatever you need to complete the move.

Thanks !

Review naming conventions

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.

Add new case of DecodeError which includes an innercase

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

Upgrade Solution to 2017 format

We're planning to continue supporting this library !

I think the first step will be to upgrade the solution:

  • use 2017 format
  • use instead latest F#+ version and so we can remove FsControl
  • reference FSharp.Core nuget
  • add literate code docs, maybe using FSharp.Formatting, although this can be in a separated PR
  • get it working in netstandard 2.0

ToJson counterpart for JsonObject.OfJson

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?

Simplify dependencies

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.

get current url

is there any way to get current url by using your fleece or any other option

Feature request, add parameters for conditional ser/de

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.

Make it easier to compose ConcreteCodecs

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.

Combinator to interpret absence of a field as an empty array

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?

  • jfieldMany
  • jfieldRepeated
  • jfieldArray

Or maybe we can upgrade the existing jfieldOpt to handle multiple values.

question: how to write the type constraints in a generic type?

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)

Fable integration

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?

CC: @alfonsogarciacaro @wallymathieu

Null value option roundtrip

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.

Documenting different ways to encode DUs

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.

Add FSharPlus.Data types

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.

Example from README does not compile

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)

Switch to Result<_,_> ?

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?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.