fsprojects / Fleece

Json mapper for F#
http://fsprojects.github.io/Fleece
Apache License 2.0
199 stars 31 forks source link

Add combinators to control the nesting in Codecs #89

Open gusty opened 4 years ago

gusty commented 4 years ago

See for instance the function proposed here

https://stackoverflow.com/questions/62287765/how-to-serialize-deserialize-this-aws-api-with-f/62291052#62291052

gusty commented 4 years ago

The other combinator we should consider adding is being used already in our tests:

let tag prop codec =
    Codec.ofConcrete codec
    |> Codec.compose (
                        (fun o -> match IReadOnlyDictionary.tryGetValue prop o with Some (JObject a) -> Ok a | _ -> Decode.Fail.propertyNotFound prop o), 
                        (fun x -> if Seq.isEmpty x then zero else Dict.toIReadOnlyDictionary (dict [prop, JObject x]))
                     )
    |> Codec.toConcrete

Here's a sample usage:

type CommandParams =
  | FooParams of arg1: int * arg2: int
  | BarParams of arg1: string
  with
    static member JsonObjCodec =
      jchoice
        [
            fun _ a -> BarParams a
            <!> jreq "method" (function BarParams _ -> Some "bar" | _ -> None)
            <*> tag "params" (
                fun _ a -> a
                <!> jreq "type" (function BarParams _ -> Some "bar.params" | _ -> None)
                <*> jreq "arg1" (function BarParams (arg1 = x) -> Some  x | _ -> None))
            fun _ (a1, a2) -> FooParams (a1, a2)
            <!> jreq "method" (function FooParams _ -> Some "foo" | _ -> None)
            <*> tag "params" (
                fun _ a1 a2 -> (a1, a2)
                <!> jreq "type" (function FooParams _ -> Some "foo.params" | _ -> None)
                <*> jreq "arg1" (function FooParams (arg1 = x) -> Some  x | _ -> None)
                <*> jreq "arg2" (function FooParams (arg2 = x) -> Some  x | _ -> None))
        ]

let jsontext1 = """
{
  "method": "foo",
  "params": {"type": "foo.params", "arg1": 1, "arg2": 2}
}"""

let jsontext2 = """
{
  "method": "bar",
  "params": {"type": "bar.params", "arg1": "beep"}
}
"""

There are simpler use cases in out tests