We are using Json.net for serialization. and we want to allow pretty much any kind of user defined class/message to be serializable.
One thing that Json.net cannot support out of the box is if you store an int
, float
or a decimal
in an object
field.
e.g.
public class SomeMessage
{
public object Something {get;set;}
}
if I store an int
, float
or decimal
in the Something field, it will be deserialized into either a long
or a double
(as json.net can not do anything more than guess the type of the primitive in the json ("foo":123 <- int or long?)
Im completely aware that I can use a correct type on the property and set various hints.
But I want to solve this for any random class/message.
My current approach is a custom JsonConverter that deals with the above types and then serializes them into a surrogate type, which holds the value as a string and a discriminator for what type this is. And then on ReadJson I turn that back into the correct type.
There is a lot of overheat for doing this. especially for the typename of the surrogate type. "foo.bar.baz.PrimitiveSurrogate, mylib"
Can I customize how this typename is stored? e.g. if I want to apply aliases to some specific types?
Are there other approaches? I could serialize the whole thing into a special string instead, which would be smaller, but then again, that feels iffy.
So what are my options here if I want to keep primitives with their correct type when stored in an untyped structure?
[Edit]
Normal Json:
{
"foo":123
}
vs.
Our current surrogate version:
{
"foo": {
"$type":"Akka.Serialization.PrimitiveSurrogate, Akka",
"V":"123",
"T":1
}
}
I could replace V and T with just `"V":"123L" and parse the suffix, as we only store int,float and decimal in that type, so we can easily have a hardcoded descriminator.
But, that still doesnt get rid of the $type for the surrogate itself, I would like to atleast shorten that to something like `"$type":"Surrogate" or something in that direction.
[Edit again] I've got it down to:
{"$type":"Akka.Util.PrimitiveSurrogate, Akka","V":"F123.456"}
But I'd really want to get rid of the long typename and replace with an alias somehow.
[Edit again again]
I've got it down to this now:
{"$":"M123.456"}
That good enough imo. We don't need to interop with any other json system, its just our framework in both ends so the made up format works even if it is not pretty.
How important is the size of the payload? You could, for example, embed metadata about the type of each primitive, essentially bundling a data contract with the data. E.g:
{
"someInteger": 123,
"$someInteger.clrType": " System.Int32"
}
EDIT:
If payload size is most important, you could do something like:
{
"someInteger.i": 123
}
That would bring down the payload increase to two characters per primitive value. I'm pretty sure the dot is safe to use as a separator; i.e. it should not be possible for a CLR identifier to have the identifier "someInteger.i", although I would double-check this against the spec (allowed identifier characters in the CLR is governed by the following doc, Annex 7 http://www.unicode.org/reports/tr15/tr15-18.html#Programming%20Language%20Identifiers).
If you really want to compact it, you could drop the separator (.) and suffix the property name with a single Unicode character which is disallowed by the CLR spec, to represent the desired primitive type. A bit hacky, but it would reduce the payload by another byte. ;-)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With