Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Surrogate int, float and decimal using Json.NET

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.

like image 877
Roger Johansson Avatar asked Mar 12 '15 12:03

Roger Johansson


1 Answers

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. ;-)

like image 137
Christian Palmstierna Avatar answered Oct 25 '22 09:10

Christian Palmstierna