JamesNK / Newtonsoft.Json

Json.NET is a popular high-performance JSON framework for .NET
https://www.newtonsoft.com/json
MIT License
10.64k stars 3.24k forks source link

Making converting serializer maintain backing "string" value #1974

Open cyberphone opened 5 years ago

cyberphone commented 5 years ago

I'm working with a scheme that must be I-JSON compatible which means that for example C#'s decimal type must be serialized as a JSON String. To do that you can use a custom JsonConverter.

However, there is for this application (clear text signed JSON) yet another requirement: maintaining a copy of the original string which is used for serialization regardless if the object was created programmatically or through JSON deserialization. This doesn't appear to be solvable by JsonConverter so I had to use this workaround:

[JsonProperty("amount")]
private string _amount;

[JsonIgnore]
public decimal Amount {
    get { return decimal.Parse(_amount); }
    set { _amount = value.ToString(); }
}

Although working it has the drawback that it requires double declarations and worst of all, leaves in-data validation to the moment you access the object.

Is there a better way doing this? If not would you consider a solution that better addresses this use-case?

danebou commented 5 years ago

yet another requirement: maintaining a copy of the original string

If this is the case, then it sounds like you require the original string in your model, as well as the decimal. So the code you posted seems sane. If you didn't then a custom JsonConverter would make sense as you said.

elgonzo commented 5 years ago

"maintaining a copy of the original string" Well, you didn't specify how exactly those copies need to be maintained. There are many ways to skin a cat. Perhaps instead of a secondary field/property, store the original strings in some dictionary (with your custom JsonConverter populating this dictionary during deserialization)? Or, if the order of the json properties from the original json object has to be maintained as well, just store the complete original json fragment representing the deserialized object in a (private) string variable?

That said, discussions like these are usually open-ended, as the definition of "better way of doing this" depends very much on your use case, your requirements and other constraints, and thus the only person to know what really is a "better way" is you.

cyberphone commented 5 years ago

The use case is here: https://tools.ietf.org/html/draft-rundgren-json-canonicalization-scheme-04 Try it out here: https://mobilepki.org/jws-jcs/home

What the best solution may be a bit subjective but since this proposal is targeted as a standard (may not happen...), it could eventually also be a standard (for .NET).

Ideally (?) you would like something similar to auto-implemented shadow variables but that requires changes all the way down to the compiler and runtime.

cyberphone commented 5 years ago

Ideally you would like this to happen on the language level using the auto-implemented backing variable:

[StringSerialize(Converter)]
public decimal Amount { get; set; }
}

The advantage would be (in addition to simplified syntax) that the custom Converter would only run during deserialization and application property assignment where it makes most sense.

TylerBrinkley commented 5 years ago

This is obviously not a Json.NET issue nor a C# issue but simply a very specific implementation requirement for your application so I believe this should be closed.

Regarding the problem couldn't you create a wrapper type for decimal that stores the value as a string but is implicitly convertible to and from a decimal?

cyberphone commented 5 years ago

This is obviously not a Json.NET issue nor a C# issue but simply a very specific implementation requirement for your application so I believe this should be closed.

This is (among many things) intended to support REST signatures. There's no standard for that. I don't know what Json.NET supports here. Open Banking APIs depend on signed REST requests, currently using proprietary methods.

I'm just investigating what the best route could possibly be. I'm not a .NET expert 😎

https://github.com/cyberphone/json-canonicalization/blob/master/REST.signatures.md

Regarding the problem couldn't you create a wrapper type for decimal that stores the value as a string but is implicitly convertible to and from a decimal?

This is the closest I could get: https://github.com/JamesNK/Newtonsoft.Json/issues/1974#issue-405985295