11

Using Json.net deserialization is there a way I can distinguish between null value and value that isn't provided i.e. missing key?

I'm considering this for partial object updates using PATCH requests and they would represent different intents:

  • Null -> set this property to null
  • Missing -> skip properties not provided

In javascript this is the difference between undefined and null.

The best I came up with for now is to use JObject.

Kugel
  • 19,354
  • 16
  • 71
  • 103

2 Answers2

12

I had the exact same issue and I stumbled on this question during my researches.

Unsatisfied by the general consensus, I created an extended Nullable struct, which I called Settable<T> for lack of imagination. Basically it is a nullable with an additional field, IsSet, which is true no matter which value you stored, as long as you stored something, and false only for default(Settable<T>), which is aliased as Settable<T>.Undefined.

A deserializer is going to write the Settable<T> only if he finds a corresponding property, in which case IsSet will be true. If the property is not included in the deserialized json, the default will be retained, which has IsSet == false. So, replacing your int? properties in your DTO type with a Settable is going to give you the required difference between undefined (or, more exactly, unspecified) and null.

Yay!

I uploaded the implementation into a repository on Github, for everyone to use (no warranty, use at your own risk, etc... I'm in hospital with 6 broken ribs and under painkillers: if something doesn't work it's YOUR fault):

https://github.com/alberto-chiesa/SettableJsonProperties

Edit: As for the comment about undefined serializing to null, I added a custom ContractResolver to handle Settable properties and updated the repository.

Alberto Chiesa
  • 7,022
  • 2
  • 26
  • 53
  • A slightly more common variation of this approach is to name it `Optional`, remove the `where T : struct` constraint and drop the references to `T?` to allow even `Optional` and such. Let the user instantiate it as `Optional` if the user wants to have undefined/null/value type. But yeah, the basic idea is good. –  Jul 18 '15 at 11:51
  • Optional? That could be a good name. I was thinking about dropping the struct restriction, but I was after a drop-in replacement for Nullable types, so I stayed as close as I could to that implementation. Thanks! – Alberto Chiesa Jul 18 '15 at 15:33
  • This works for deserialization, but doesn't work for serialization. Serialiazation of Settable.Undefined returns 'null'. Eg: if I have a class Tmp, like you do in your tests, and I deserialized "{A:10}" then serialize the result, the result will be "{A:10,B:null}". Any ideas on how to emit a property from a class on serialization when Settable.IsSet is false? – Tom Heard Nov 08 '15 at 23:58
  • Hi Tom. Yes, I didn't worry about serialization, because with my use I always have a .Net backend and a JavaScript client, so I read data from a db and "undefined" doesn't happen. Null, yes, but undefined, no. You will need a custom ContractResolver to do what you want. I made a Github repository, with the filed from this answer and the ContractResolver you are looking for. Cheers! – Alberto Chiesa Nov 10 '15 at 09:43
  • @A.Chiesa I guess you did not test the case with Settable. I tried it and got exception 'Additional text found in JSON string after finishing deserializing object'. Do you have a solution for that? – Ha Pham Jan 30 '19 at 09:17
  • @Fanliver I used it with various types. I guess the error you report has nothing to do with the use of `settable`. Usually it is related to an incorrectly formatted Json, like so: `{"prop1":"A","prop2":10}{"prop1":"B"}`. This Json is composed of TWO objects, one after the other. As soon as Json.NET encounters the `{` of the second object, it throws the exception you refer to. Or something like that. If you found the answer useful, upvote ;) – Alberto Chiesa Jan 30 '19 at 09:49
  • @A.Chiesa This is how I used your code: https://dotnetfiddle.net/jRsqG3. I don't think the JSON string has a problem. – Ha Pham Jan 30 '19 at 10:05
  • @Fanliver: I don't think, too. I'm going to take a deeper look at it later. – Alberto Chiesa Jan 30 '19 at 10:28
  • @Fanliver you're right. The problem stems from the fact that the `JsonReader` Value is null, when trying to deserialize Address. Because the TokenType is `StartObject`, instead of being an actual value. I'm reasoning on how to manage that case, that should fall back to the base JsonConverter implementation.. – Alberto Chiesa Jan 31 '19 at 09:12
  • @A.Chiesa Do you have any solution for that case so far? I do not know much about JsonConverter under the hood. – Ha Pham Feb 04 '19 at 09:18
  • @Fanliver nope. I have only the idea for a solution, but no implementation so far. Didn't get the time to dig into it so far – Alberto Chiesa Feb 04 '19 at 09:20
0

The answer here seems to be what you are after: https://stackoverflow.com/a/21820736/39532

The basic summary is:

  • undefined isn't directly supported by JSON since not setting a property is functionally equivalent
  • Null may be represented by new JValue((object)null)
Community
  • 1
  • 1
mezoid
  • 28,090
  • 37
  • 107
  • 148
  • That's an opposite problem. I'm receiving not sending. Although it confirmed what I thought. – Kugel Mar 04 '14 at 00:38
  • @Kugel Yeah, I know the question in that link is a bit different from what you are after...but I thought the accepted answer did give a better explanation of the difference between null and undefined in JSon.Net which will hopefully point you in the right direction. – mezoid Mar 04 '14 at 00:43