Home > database >  Null propagation in JToken
Null propagation in JToken

Time:10-25

So I have a JToken. It might have (or not) a JToken inside. Let's call it "client".

it can be null.

Right now,I'm using this code to check for existance and null :

var client = MyJToken["client"];
if (client != null && client is not JValue)

But it just feels wrong. Why can't I use "MyJToken["client"] ?? "

Anyone has an idead how to to that check using null propagation ?

Edit Example.

So let’s say you have this JSON :

https://pastebin.com/nSpP6LEQ

In “invoiceds” array, you’ll see the first is null, but second has value.

The JToken is at the level of one invoiceds entry.

So the naïve thing to write would be mytoken[“task”]?[“id”]

But that will fail, because the first record is null, so mytoken[“task”] is NOT NULL, but has a JValue of null.

So my question is : Is there anyway avoid to that check for JValue and null. And write something close to mytoken[“task”]?[“id”].

CodePudding user response:

Maybe you need to perform something like this:

using Newtonsoft.Json.Linq;

namespace ConsoleApp1
{
  internal class Program
  {
    static void Main(string[] args)
    {
      var json = "[{\"foo\": \"bar\"},{\"task\": null}, {\"task\": 23}, {\"task\": {\"inner\": true}}]";

      var jArray = JArray.Parse(json);

      foreach (var item in jArray.Children())
      {
        if (item is JObject obj) 
        {
          DoSomething(obj);
        }
      }
    }

    static void DoSomething(JObject obj)
    {
      var token = obj["task"];

      if (token is null) 
      {
        Console.WriteLine("'task' property is missing");
      }
      else if (token is JValue jValue) 
      {
        if (jValue.Type == JTokenType.Null)
        {
          Console.WriteLine("'task' property is inside the provided object. its value is json NULL");
        }
        else 
        {
          Console.WriteLine($"'task' property is inside the provided object. its value is {jValue.Value}");
        }
      }
      else if (token is JObject jObject) 
      {
        Console.WriteLine("'task' property is inside the provided object. its value is an object");
      }
    }
  }
}

CodePudding user response:

There's not much you could do when operating on the token level because there should be a way to distinguish a JSON field being unspecified and being set to null. Which is MyJToken["client"] is null in the first scenario and MyJToken["client"].Type == JTokenType.Null in the second.

Best thing you could do here is to write an extension method, something like:

internal static class JsonExtensions
{
    public static void HasValue(this JToken token) => token is { Type: not JTokenType.Null };
}

And then use it like this:

if (MyJToken["client"].HasValue())
{
  //...
}

This also works with null propagation:

if (token["x"]?["y"]?["z"].HasValue())
{
  //...
}

However, what you could also do is to deserialize your JSON into an object. In that case the property you are looking for would be set to null both in case it was unspecified and in case it was explicitly set to null value. I would suggest this approach because you are most likely going to read the property from your model anyway. And you might need other properties as well.

Take a look here to see how it could be implemented. There also are a bunch of C# model generators that would create model classes for you based on your JSON. For example here or here

Also Visual Studio 2022 can create model classes for you as well if you do Edit -> Paste Special -> Paste JSON as Classes. More info here

  • Related