-
Notifications
You must be signed in to change notification settings - Fork 44
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
Serialize Tuple as an object #116
Comments
To be clear, you would like something like this? JsonSerializer.Serialize [("name", "foo"); ("other", "bar")]
// --> [{"name":"foo"},{"other":"bar"}] I think that's a bit too specific and niche to include in FSharp.STJ. But you can absolutely implement a dedicated open System.Text.Json
open System.Text.Json.Serialization
open FSharp.Core.CompilerServices
type LookupListConverter<'V>() =
inherit JsonConverter<(string * 'V) list>()
override this.Read(reader, typeToConvert, options) =
if reader.TokenType <> JsonTokenType.StartArray then raise (JsonException "Expected array")
let mutable l = ListCollector()
while reader.Read() && reader.TokenType <> JsonTokenType.EndArray do
if reader.TokenType <> JsonTokenType.StartObject then raise (JsonException "Expected object")
if not (reader.Read() && reader.TokenType = JsonTokenType.PropertyName) then raise (JsonException "Expected non-empty object")
let key = reader.GetString()
let value = JsonSerializer.Deserialize<'V>(&reader, options)
if not (reader.Read() && reader.TokenType = JsonTokenType.EndObject) then raise (JsonException "Expected single-property object")
l.Add((key, value))
l.Close()
override this.Write(writer, value, options) =
writer.WriteStartArray()
for k, v in value do
writer.WriteStartObject()
writer.WritePropertyName(k)
JsonSerializer.Serialize<'V>(writer, v, options)
writer.WriteEndObject()
writer.WriteEndArray()
type LookupListConverter() =
inherit JsonConverterFactory()
override this.CanConvert(typeToConvert) =
typeToConvert.IsGenericType &&
typeToConvert.GetGenericTypeDefinition() = typedefof<_ list> &&
let tparam = typeToConvert.GetGenericArguments()[0]
tparam.IsGenericType &&
tparam.GetGenericTypeDefinition() = typedefof<_ * _> &&
tparam.GetGenericArguments()[0] = typeof<string>
override this.CreateConverter(typeToConvert, options) =
let tparam = typeToConvert.GetGenericArguments().[0].GetGenericArguments().[1]
typedefof<LookupListConverter<_>>.MakeGenericType(tparam)
.GetConstructor([||])
.Invoke([||])
:?> JsonConverter
// Usage:
let options = JsonSerializerOptions()
options.Converters.Add(ListLookupConverter())
JsonSerializer.Serialize([("name", "foo"); ("other", "bar")], options) |
Quite often, two-tuples are used in (unique or not) KV setting, similar to how the I usually work around this limitation by using a record instead of a tuple, but it’d be nice if this were configurable. I do get the “it’s too niche” comment though, but is it really? ;). |
Well, I was basing it on the fact that I don't think I've ever seen this kind of JSON in the wild, but apparently it's common enough that multiple people are requesting it 😄 We could add an option for this. What should it apply to exactly though? Any |
@Tarmil Any enumerable seems reasonable (not just It’ll be behind an option, so if we want different behaviour, like people wanting this for all tuples, we can enable that explicitly later. @isaacabraham you wrote the OP, what do you think? |
It would be nice to be able to serialize a tuple directly to an object rather than a list e.g. ("name", "foo") => { "name" : "foo" }. I can't see if there's any way to do that. My use case is that I want to serialize a list of tuples to JSON in the above style rather than as a list of lists. I can use a Map / Dictionary here, except that that enforces uniqueness of the key, which I don't want.
Any ideas?
The text was updated successfully, but these errors were encountered: