Skip to content

Improve union case serialization & added tests #29

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion FSharp.Json.Tests/Union.fs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ module Union =

type TheUnion =
| OneFieldCase of string
| ManyFieldsCase of string*int
| ManyFieldsCase of field1:string*field2:int
| RecordCase of TheRecord

[<Test>]
Expand All @@ -19,13 +19,27 @@ module Union =
let expected = """{"OneFieldCase":"The string"}"""
Assert.AreEqual(expected, actual)

[<Test>]
let ``Union two field case serialization`` () =
let value = ManyFieldsCase("The string", 2)
let actual = Json.serializeU value
let expected = """{"ManyFieldsCase":{"field1":"The string","field2":2}}"""
Assert.AreEqual(expected, actual)

[<Test>]
let ``Union one field case deserialization`` () =
let expected = OneFieldCase "The string"
let json = Json.serialize(expected)
let actual = Json.deserialize<TheUnion> json
Assert.AreEqual(expected, actual)

[<Test>]
let ``Union two field case deserialization`` () =
let expected = ManyFieldsCase("The string", 2)
let json = Json.serialize(expected)
let actual = Json.deserialize<TheUnion> json
Assert.AreEqual(expected, actual)

[<Test>]
let ``Union many fields case serialization`` () =
let expected = ManyFieldsCase ("The string", 123)
Expand Down
16 changes: 14 additions & 2 deletions FSharp.Json/Core.fs
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,9 @@ module internal Core =
let caseType = types.[0]
serializeUnwrapOptionWithNull caseType jsonField caseValue
| _ ->
serializeTupleItems types values
let props: PropertyInfo array = caseInfo.GetFields()
let fields = props |> Array.map (serializeProperty theunion) |> Array.choose id
JsonValue.Record fields
let unionCases = getUnionCases caseInfo.DeclaringType
match unionCases.Length with
| 1 -> jvalue
Expand Down Expand Up @@ -519,7 +521,17 @@ module internal Core =
[| propValue |]
| _ ->
let propsTypes = props |> Array.map (fun p -> p.PropertyType)
deserializeTupleElements casePath propsTypes fieldValue
match fieldValue with
| JsonValue.Record(jsonFields) ->
let fields = jsonFields |> Map.ofArray
props
|> Array.map(fun prop ->
match prop.Name |> fields.TryFind with
| Some value ->
deserializeUnwrapOption path prop.PropertyType JsonField.Default (Some value)
| None -> failDeserialization path "Failed to parse union from JSON because field %s of case %s is missing." prop.Name caseInfo.Name
)
| _ -> failDeserialization path "Failed to parse union from JSON that is not object."
FSharpValue.MakeUnion (caseInfo, values)
| _ -> failDeserialization path "Failed to parse union from JSON that is not object."

Expand Down
8 changes: 8 additions & 0 deletions FSharp.Json/Interface.fs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ module Json =
let value = JsonValue.Parse(json)
(Core.deserialize config JsonPath.Root typeof<'T> value) :?> 'T

let deserializeExDynamic (typ: Type) (config: JsonConfig) (json: string): obj =
let value = JsonValue.Parse(json)
Core.deserialize config JsonPath.Root typ value


/// Serailizes F# object into JSON. Uses default [JsonConfig].
let serialize (theobj: obj) = serializeEx JsonConfig.Default theobj

Expand All @@ -49,3 +54,6 @@ module Json =

/// Deserailizes JSON into F# type provided as generic parameter. Uses default [JsonConfig].
let deserialize<'T> (json: string) = deserializeEx<'T> JsonConfig.Default json

/// Deserailizes JSON into F# type provided as generic parameter. Uses default [JsonConfig].
let deserializeDynamic typ (json: string) = deserializeExDynamic typ JsonConfig.Default json