Skip to content
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
21 changes: 21 additions & 0 deletions FSharp.Json.Tests/Collections.fs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ module Collections =
let actual = Json.serializeU value
Assert.AreEqual(expected, actual)

[<Test>]
let ``ResizeArray serialization to JSON array`` () =
let expected = """["some","text"]"""
let value = [ "some"; "text" ] |> ResizeArray
let actual = Json.serializeU value
Assert.AreEqual(expected, actual)

[<Test>]
let ``Array serialization/deserialization`` () =
let expected = [|"some"; "text"|]
Expand All @@ -32,6 +39,13 @@ module Collections =
let actual = Json.deserialize<string list> json
Assert.AreEqual(expected, actual)

[<Test>]
let ``ResizeArray serialization/deserialization`` () =
let expected = [ "some"; "text" ] |> ResizeArray
let json = Json.serialize (expected)
let actual = Json.deserialize<ResizeArray<string>> json
Assert.AreEqual(expected, actual)

[<Test>]
let ``Array empty serialization/deserialization`` () =
let expected = [||]
Expand All @@ -45,3 +59,10 @@ module Collections =
let json = Json.serialize(expected)
let actual = Json.deserialize<string list> json
Assert.AreEqual(expected, actual)

[<Test>]
let ``ResizeArray empty serialization/deserialization`` () =
let expected = ResizeArray<string>()
let json = Json.serialize (expected)
let actual = Json.deserialize<ResizeArray<string>> json
Assert.AreEqual(expected, actual)
21 changes: 18 additions & 3 deletions FSharp.Json/Core.fs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@
let enumMode = getEnumMode config jsonField
match enumMode with
| EnumMode.Value ->
match baseT with

Check warning on line 81 in FSharp.Json/Core.fs

View workflow job for this annotation

GitHub Actions / build

Incomplete pattern matches on this expression.

Check warning on line 81 in FSharp.Json/Core.fs

View workflow job for this annotation

GitHub Actions / build

Incomplete pattern matches on this expression.
| t when t = typeof<int> ->
let enumValue = decimal (value :?> int)
JsonValue.Number enumValue
Expand Down Expand Up @@ -149,7 +149,7 @@
JsonValue.String ((value :?> Guid).ToString())
| t when t.IsEnum ->
serializeEnum t jsonField value
| t when isTuple t || isList t || isArray t || isMap t || isRecord t || isUnion t ->
| t when isTuple t || isList t || isArray t || isMap t || isRecord t || isUnion t || isResizeArray t ->
serialize config t value
| _ -> failSerialization $"Unknown type: %s{t.Name}"
| true ->
Expand Down Expand Up @@ -265,6 +265,7 @@
| t when isList t -> serializeEnumerable (value :?> IEnumerable)
| t when isTuple t -> serializeTupleItems (getTupleElements t) (FSharpValue.GetTupleFields value)
| t when isUnion t -> serializeUnion t value
| t when isResizeArray t -> serializeEnumerable (value :?> IEnumerable)
| t ->
let msg = $"Failed to serialize, must be one of following types: record, map, array, list, tuple, union. Type is: %s{t.Name}."
failSerialization msg
Expand Down Expand Up @@ -299,7 +300,7 @@
let enumMode = getEnumMode config jsonField
match enumMode with
| EnumMode.Value ->
match baseT with

Check warning on line 303 in FSharp.Json/Core.fs

View workflow job for this annotation

GitHub Actions / build

Incomplete pattern matches on this expression.

Check warning on line 303 in FSharp.Json/Core.fs

View workflow job for this annotation

GitHub Actions / build

Incomplete pattern matches on this expression.
| baseT when baseT = typeof<int> ->
let enumValue = JsonValueHelpers.getInt path jValue
Enum.ToObject(t, enumValue)
Expand Down Expand Up @@ -374,7 +375,7 @@
JsonValueHelpers.getGuid path jValue :> obj
| t when t.IsEnum ->
deserializeEnum path t jsonField jValue
| t when isTuple t || isList t || isArray t || isMap t || isRecord t || isUnion t ->
| t when isTuple t || isList t || isArray t || isMap t || isRecord t || isUnion t || isResizeArray t ->
deserialize config path t jValue
| _ -> failDeserialization path $"Not supported type: %s{t.Name}"
transformFromTargetType jsonField.Transform jValue
Expand Down Expand Up @@ -431,6 +432,19 @@
arrayValues |> List.ofSeq |> createList itemType
| _ -> failDeserialization path "Failed to parse list from JSON that is not array."

let deserializeResizeArray (path: JsonPath) (t: Type) (jvalue: JsonValue) : obj =
match jvalue with
| JsonValue.Array jvalues ->
let itemType = getResizeArrayItemType t

let arrayValues =
deserializeArrayItems path itemType jvalues

arrayValues
|> List.ofSeq
|> createResizeArray itemType
| _ -> failDeserialization path "Failed to parse resize array from JSON that is not array."

let deserializeArray (path: JsonPath) (t: Type) (jvalue: JsonValue): obj =
match jvalue with
| JsonValue.Array jvalues ->
Expand Down Expand Up @@ -522,7 +536,7 @@
| JsonValue.String caseName ->
FSharpValue.MakeUnion (caseName |> getUnionCaseInfo path t, null)
| JsonValue.Record fields ->
match jsonUnion.Mode with

Check warning on line 539 in FSharp.Json/Core.fs

View workflow job for this annotation

GitHub Actions / build

Enums may take values outside known cases. For example, the value 'enum<UnionMode> (3)' may indicate a case not covered by the pattern(s).

Check warning on line 539 in FSharp.Json/Core.fs

View workflow job for this annotation

GitHub Actions / build

Enums may take values outside known cases. For example, the value 'enum<UnionMode> (3)' may indicate a case not covered by the pattern(s).
| UnionMode.CaseKeyDiscriminatorField ->
let caseKeyFieldName, caseKeyFieldValue = mustFindField path jsonUnion.CaseKeyField fields
let caseNamePath = caseKeyFieldName |> JsonPathItem.Field |> path.createNew
Expand All @@ -549,5 +563,6 @@
| t when isList t -> deserializeList path t jValue
| t when isTuple t -> deserializeTuple path t jValue
| t when isUnion t -> deserializeUnion path t jValue
| t when isResizeArray t -> deserializeResizeArray path t jValue
| _ -> failDeserialization path $"Failed to serialize, must be one of following types: record, map, array, list, tuple, union. Type is: %s{t.Name}."

36 changes: 36 additions & 0 deletions FSharp.Json/Reflection.fs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,19 @@ module internal Reflection =
let getListEmptyProperty_ (t: Type) =
t.GetProperty("Empty")

let isResizeArray_ (t: Type) =
t.IsGenericType
&& t.GetGenericTypeDefinition() = typedefof<ResizeArray<_>>

let getResizeArrayType_ (itemType: Type) =
typedefof<ResizeArray<_>>.MakeGenericType ([| itemType |])

let getResizeArrayItemType_ (t: Type) = t.GetGenericArguments().[0]

let getResizeArrayConstructor_ (t: Type) = t.GetConstructor([||])

let getResizeArrayAdd_ (t: Type) = t.GetMethod("Add")

let isMap_ (t: Type) =
t.IsGenericType && t.GetGenericTypeDefinition() = typedefof<Map<_,_>>

Expand Down Expand Up @@ -72,6 +85,13 @@ module internal Reflection =
let getListConstructor: Type -> MethodInfo = getListConstructor_ |> cacheResult
let getListEmptyProperty: Type -> PropertyInfo = getListEmptyProperty_ |> cacheResult

let isResizeArray : Type -> bool = isResizeArray_ |> cacheResult
let getResizeArrayType : Type -> Type = getResizeArrayType_ |> cacheResult
let getResizeArrayItemType : Type -> Type = getResizeArrayItemType_ |> cacheResult
let getResizeArrayAdd : Type -> MethodInfo = getResizeArrayAdd_ |> cacheResult
let getResizeArrayConstructor : Type -> ConstructorInfo =
getResizeArrayConstructor_ |> cacheResult

let isMap: Type -> bool = isMap_ |> cacheResult
let getMapKeyType: Type -> Type = getMapKeyType_ |> cacheResult
let getMapValueType: Type -> Type = getMapValueType_ |> cacheResult
Expand All @@ -98,6 +118,22 @@ module internal Reflection =
let theList = (getListEmptyProperty listType).GetValue(null)
List.foldBack addItem items theList

let createResizeArray (itemType: Type) (items: obj list) =
let resizeArrayType = getResizeArrayType itemType

let resizeArrayAdd resizeArray item =
(getResizeArrayAdd resizeArrayType)
.Invoke(resizeArray, [| item |])
|> ignore

resizeArray

let newResizeArray =
(getResizeArrayConstructor resizeArrayType)
.Invoke([||])

List.fold resizeArrayAdd newResizeArray items

let KvpKey (value: obj): obj =
let keyProperty = value.GetType().GetProperty("Key")
keyProperty.GetValue(value, null)
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ option | option is not represented by itself<br>`None` value might be represente
tuple | list
record | object
map | object
array<br>list | list
array<br>list<br>ResizeArray | list
union | object with special structure<br>read more in [Unions](#unions) section
obj | read [Untyped Data](#untyped-data) section

Expand Down
Loading