i am new to F#, and have a task to serialize a Discriminated Union. That in itself is simple, you can serialize it, and you'll get a nice (ugly) case: fields: set of nodes.
Using the Compact Serializer from FSharpLu (https://github.com/Microsoft/fsharplu) we can get this down to a more manageable format, without Case: Field: nodes. However, instead, we get the name of the Type that was serialized as the nodes name. This is not wanted.
If i have a types such as:
type Entry = {
id: string
foo: int
bar: string
}
type Group = {
id: string
entryList: Entry seq
}
type EntryOrGroup =
| Entry of Entry
| Group of Group
In the output, an array of EntryOrGroup, i would get each node decorated with its type name, which i do not want.
Is there a way to hide the type name? but still output the desired json nodes?
EDIT: Example of current output:
[
{
Group: {
id: "firstEntry",
entryList: [
{
id: "foobar",
foo: 12,
bar: "something"
},
{
id: "barfoo",
foo: 13,
bar: "somethingElse"
}
]
},
{
Entry: {
id: "foofoobar",
foo: 16,
bar: "notSomething"
}
}
}
]
Example of Desired Output:
[
{
{
id: "firstEntry",
entryList: [
{
id: "foobar",
foo: 12,
bar: "something"
},
{
id: "barfoo",
foo: 13,
bar: "somethingElse"
}
]
},
{
{
id: "foofoobar",
foo: 16,
bar: "notSomething"
}
}
}
]
Edit2: I forked the FSharpLu repo, and changed the Compact Serializer to no longer write the start of objects and the end of objects in a Discriminated Union. This achieves the result i'm looking for. However, i don't know right now how i can implement that in my work... Is a forked repo too much to maintain, is it worth it for this tiny change? Hmm...
Changes were from Line 146 of Compact.fs: From:
else
let case, fields = getUnionFields value
match fields with
// Field-less union case
| [||] -> writer.WriteValue(convertName case.Name)
// Case with single field
| [|onefield|] ->
writer.WriteStartObject()
writer.WritePropertyName(convertName case.Name)
serializer.Serialize(writer, onefield)
writer.WriteEndObject()
// Case with list of fields
| _ ->
writer.WriteStartObject()
writer.WritePropertyName(convertName case.Name)
serializer.Serialize(writer, fields)
writer.WriteEndObject()
To:
else
let case, fields = getUnionFields value
match fields with
// Field-less union case
| [||] -> writer.WriteValue(convertName case.Name)
// Case with single field
| [|onefield|] ->
serializer.Serialize(writer, onefield)
// Case with list of fields
| _ ->
writer.WritePropertyName(convertName case.Name)
serializer.Serialize(writer, fields)
Here are few alternatives that you can consider if you don't want to write/maintain custom Json.NET converter:
1. Use some other serialization library
Two alternatives that give you more control how data is serialized:
2. Use data transfer objects that are serialized as you want
type EntryOrGroupDTO = {
id: string
foo: int option
bar: string option
entryList: Entry seq option
}
let dtoData =
data
|> Array.map (fun entryOrGroup ->
match entryOrGroup with
| Entry entry ->
{ id = entry.id
foo = Some entry.foo
bar = Some entry.bar
entryList = None }
| Group group ->
{ id = group.id
foo = None
bar = None
entryList = Some group.entryList })
let settings =
JsonSerializerSettings(
NullValueHandling=NullValueHandling.Ignore,
Converters=[|CompactUnionJsonConverter()|]
)
JsonConvert.SerializeObject(dtoData, Formatting.Indented, settings)
3. Unwarp and box
let objData =
data
|> Array.map (fun entryOrGroup ->
match entryOrGroup with
| Entry entry -> box entry
| Group group -> box group)
JsonConvert.SerializeObject(objData, Formatting.Indented)