The scenario is - receiving a JSON string from the network and deserializing it into correct corresponding record type.
JSON string can be either:
(1) "{"a":"some text"}"
or
(2) "{"b":1}"
Values might differ, but the format of fields will either be corresponding to Type1 or Type2:
type Type1 = {a:string}
type Type2 = {b:int}
When receiving an unknown string, I'm trying to get an instance of a correct record type:
// Contents of a string might be like (1) or like (2), but we don't know which one
let someJsonString = "..."
let obj = JsonConvert.DeserializeObject(someJsonString)
The last line returns an object of type Object.
Using pattern matching on it doesn't determine the type:
match obj with
| :? Type1 as t1 -> printfn "Type1: a = %A" t1.a
| :? Type2 as t2 -> printfn "Type2: b = %A" t2.b
| _ -> printfn "None of above"
Here the "None of above" is printed.
When I deserialize an object using indicating some type:
JsonConvert.DeserializeObject<Type1>(someJsonString)
Pattern matching is working and printing:
Type1: a = <the value of a>
However, that doesn't work in my case, because I can't know in advance what type of contents unknown JSON string is going to have.
Is there any way to deserialize a JSON string into correct record type based on the string contents?
Note: If necessary, when the string is serialized on the side where it's being sent, the name of the type can be sent as a part of this string. But then, how to get an instance of a Type type, having a name of a type, like "Type1" or "Type2"?
The fully qualified name of the type will be different on different machines, so I'm not sure if it's possible. I.e. one machine will have Type1 specified as:
"FSI_0059+Test1, FSI-ASSEMBLY, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"
And another one as:
"FSI_0047+Test1, FSI-ASSEMBLY, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"
You cannot do this generically without additional type information. The type would need to be specified when serializing, so that it may be read back when deserializing.
Newtonsoft.Json
has a TypeNameHandling
option that you can set when serialzing, so that the resulting JSON deserializes into the correct type.
Here's a complete example:
let instance = { a = 10 }
let settings = new JsonSerializerSettings(TypeNameHandling = TypeNameHandling.All)
let json = JsonConvert.SerializeObject(instance, settings)
let retrieved = JsonConvert.DeserializeObject(json, settings)