This is by design.
Json.NET never allows an actual null JToken
value to appear in a JToken
hierarchy, either as an JArray
member or a JProperty
value. Instead, if applications code attempts to set or add a null
token, it gets replaced with a non-null JValue
with JValue.Type
equal to JTokenType.Null
. This replacement occurs in, e.g., JContainer.EnsureParentToken(JToken item, bool skipParentCheck)
:
internal JToken EnsureParentToken(JToken item, bool skipParentCheck)
{
if (item == null)
{
return JValue.CreateNull();
}
As well as JProperty.Value
:
public JToken Value
{
set
{
CheckReentrancy();
JToken newValue = value ?? JValue.CreateNull();
I believe Newtonsoft does this to capture the difference between the following two JSON objects:
{
"field1": null
}
And the empty object:
{ }
In the first case, the property "field1"
is present with a null
JSON value. In the second case, the property "field1"
is not present. Linq-to-JSON represents the first case with a null-type JValue
rather than having JProperty.Value
actually be null. It likely does this because if it did not, object["field1"]
would return null
in both cases, making them harder to distinguish. (Unlike Dictionary<TKey, TValue>
, JObject
does not throw a KeyNotFoundException
when attempting to access the value of a non-existent property. Instead, JObject.Item[String]
returns a null JValue
for the value of a missing key.)
Your code, obj["field1"] = null;
, creates a JSON object of the first form, which you can see if you examine the value of obj.ToString()
. Thus obj["field1"]
returns non-null
If you do not need to distinguish between missing and null-valued properties, you could introduce an extension method to check for a null value such as the one of the ones from Checking for empty/null JToken in a JObject or Issue with JSON null handling in Newtonsoft.