Nullable Syntax in Queried Object Properties

  • Nullable syntax can be used with model class property types (with both C# reference types as well as value types such as System.Double) for classes used as return types in query result.

  • Nullable syntax can be used for item types as well in model class properties that are of one of the following types:
    • System.Collections.Generic.List<T>

    • System.Collections.Generic.IReadOnlyList<T>

    • System.Collections.Generic.IEnumerable<T>

    • Arrays

  • JsonQL will evaluate all non-nullable property values and will report conversion errors of type JsonQL.JsonToObjectConversion.IConversionError in result with value of ErrorType equal to one of the following enum values: NonNullablePropertyNotSet, NonNullableCollectionItemValueNotSet.

In example below we query for a non-nullable collection of non-nullable IEmployee items.

  • If the query results in IEmployee instances with null value for Address property of non-nullable type IAddress, of if any instance of IEmployee in query result has null value for IEmployee.Address.Street (IAddress.Street is of non-nullable string type), JsonQL will report conversion error(s) of type JsonQL.JsonToObjectConversion.IConversionError in result with value of ErrorType equal to one of the following enum values: NonNullablePropertyNotSet.

Click to expand Company.json used in query in example below
{
  "Companies": [
    {
      "CompanyData": {
        "Name": "Strange Things, Inc",
        "CEO": "John Malkowich",
        "Address": {
          "Street": "123 Maple Street",
          "City": "Springfield",
          "State": "IL",
          "ZipCode": "62701"
        },
        "CountryDetails": "$value(Countries.Where(x => x.Name == 'United States'))"
      },
      "Employees": [
        {
          "Id": 100000001,
          "FirstName": "John",
          "LastName": "Smith",
          "Address": {
            "Street": "456 Oak Avenue",
            "City": "Chicago",
            "State": "IL",
            "ZipCode": "60601"
          },
          "Salary": 99500,
          "Age": 45,
          "Phones": [
            "312-555-0134",
            "312-555-0178"
          ]
        },
        {
          "Id": 100000002,
          "FirstName": "Alice",
          "LastName": "Johnson",
          "Address": {
            "Street": "123 Maple Street",
            "City": "New York",
            "State": "NY",
            "ZipCode": "10001"
          },
          "Salary": 105000,
          "Age": 38,
          "Phones": [
            "212-555-0199"
          ]
        },
        {
          "Id": 100000003,
          "FirstName": "Michael",
          "LastName": "Brown",
          "Address": {
            "Street": "789 Pine Lane",
            "City": "Los Angeles",
            "State": "CA",
            "ZipCode": "90001"
          },
          "Salary": 89000,
          "Age": 50,
          "Phones": []
        },
        {
          "Id": 100000004,
          "FirstName": "Emily",
          "LastName": "Davis",
          "Address": {
            "Street": "321 Elm Drive",
            "City": "Houston",
            "State": "TX",
            "ZipCode": "77001"
          },
          "Salary": 92000,
          "Age": 42,
          "Phones": [
            "713-555-0147",
            "713-555-0112"
          ]
        }
      ]
    },
    {
      "CompanyData": {
        "Name": "Sherwood Forest Timber, Inc",
        "CEO": "Robin Wood",
        "Address": {
          "Street": "789 Pine Lane",
          "City": "Denver",
          "State": "CO",
          "ZipCode": "80203"
        },
        "CountryDetails": "$value(Countries.Where(x => x.Name == 'Germany'))"
      },
      "Employees": [
        {
          "Id": 100000005,
          "FirstName": "Christopher",
          "LastName": "Garcia",
          "Address": {
            "Street": "654 Cedar Road",
            "City": "Phoenix",
            "State": "AZ",
            "ZipCode": "85001"
          },
          "Salary": 111000,
          "Age": 29,
          "Logins": [
            "cgarcia@sherwood.com",
            "cgarcia@gmail.com"
          ],
          "Phones": [
            "602-555-0166",
            "602-555-0188"
          ]
        },
        {
          "Id": 100000006,
          "FirstName": "Sarah",
          "LastName": "Wilson",
          "Address": null,
          "Salary": 78000,
          "Age": 35,
          "Phones": []
        },
        {
          "Id": 100000007,
          "FirstName": "David",
          "LastName": "Martinez",
          "Address": {
            "Street": "147 Birch Street",
            "City": "San Antonio",
            "State": "TX",
            "ZipCode": "78201"
          },
          "Salary": 95000,
          "Age": 46,
          "Logins": [
            "dmartinez@sherwood.com",
            "dmartinez@gmail.com"
          ],
          "Phones": [
            "210-555-0123"
          ]
        }
      ]
    },
    {
      "CompanyData": {
        "Name": "Atlantic Transfers, Inc",
        "CEO": "Black Beard",
        "Address": {
          "Street": "101 Elm Drive",
          "City": "Dallas",
          "State": "TX",
          "ZipCode": "75201"
        },
        "CountryDetails": "$value(Countries.Where(x => x.Name == 'United States'))"
      },
      "Employees": [
        {
          "Id": 100000008,
          "FirstName": "Laura",
          "LastName": "Lee",
          "Address": {
            "Street": "258 Willow Lane",
            "City": "San Diego",
            "State": "CA",
            "ZipCode": "92101"
          },
          "Salary": 105500,
          "Age": 32,
          "Phones": [
            "619-555-0155",
            "619-555-0122"
          ]
        },
        {
          "Id": 100000009,
          "FirstName": "Andrew",
          "LastName": "Harris",
          "Address": {
            "Street": "369 Spruce Drive",
            "City": "Dallas",
            "State": "TX",
            "ZipCode": "75201"
          },
          "Salary": 88000,
          "Age": 41,
          "Phones": [
            "214-555-0180"
          ]
        },
        {
          "Id": 100000010,
          "FirstName": "Jessica",
          "LastName": "Thompson",
          "Address": {
            "Street": "159 Cherry Lane",
            "City": "Austin",
            "State": "TX",
            "ZipCode": "73301"
          },
          "Salary": 98700,
          "Age": 37,
          "Phones": []
        },
        {
          "Id": 250150245,
          "FirstName": "Jane",
          "LastName": "Doe",
          "Address": {
            "Street": "Main St",
            "City": "San Jose",
            "State": "PA",
            "ZipCode": "95101"
          },
          "Salary": 144186,
          "Age": 63,
          "Phones": [
            "408-555-0133",
            "408-555-0190"
          ]
        },
        {
          "Id": 783328759,
          "FirstName": "Robert",
          "LastName": "Brown",
          "Address": {
            "Street": "Pine St",
            "City": "Los Angeles",
            "State": "CA",
            "ZipCode": "90001"
          },
          "Salary": 122395,
          "Age": 58,
          "Phones": [
            "323-555-0177"
          ]
        }
      ]
    }
  ]
}


// Select the employees in all companies with non-null value for Address
var query =
    "Companies.Select(c => c.Employees).Where(e => e.Address is not null)";

// Set the value of queryManager to an instance of JsonQL.Query.IQueryManager here.
// The value of JsonQL.Query.IQueryManager is normally created by Dependency Injection container
// and it is normally configured as a singleton.
JsonQL.Query.IQueryManager queryManager = null!;

// Employees in employeesResult.Value of type IReadOnlyList<IEmployee> can have null for
// property IEmployee.Manager since this property is of type "IManager?" (uses nullable syntax)
// Also, the values of IEmployee.Address.County can be null in result too
// since the property IAddress.County is of type "string?" (nullable string)
var employeesResult =
    queryManager.QueryObject<IReadOnlyList<IEmployee>>(query,
        new JsonTextData("Companies",
            LoadJsonFileHelpers.LoadJsonFile("Companies.json", ["DocFiles", "QueryingJsonFiles", "JsonFiles"])));
Click to expand the result of the query in example above (i.e., instance of JsonQL.Query.IObjectQueryResult<IReadOnlyList<IEmployee>>) serialized into Result.json
{
  "$type": "JsonQL.Query.ObjectQueryResult`1[[System.Collections.Generic.IReadOnlyList`1[[JsonQL.Demos.Examples.DataModels.IEmployee, JsonQL.Demos]], System.Private.CoreLib]], JsonQL",
  "Value": {
    "$type": "System.Collections.Generic.List`1[[JsonQL.Demos.Examples.DataModels.IEmployee, JsonQL.Demos]], System.Private.CoreLib",
    "$values": [
      {
        "$type": "JsonQL.Demos.Examples.DataModels.Employee, JsonQL.Demos",
        "Id": 100000001,
        "FirstName": "John",
        "LastName": "Smith",
        "Address": {
          "$type": "JsonQL.Demos.Examples.DataModels.Address, JsonQL.Demos",
          "Street": "456 Oak Avenue",
          "City": "Chicago",
          "State": "IL",
          "ZipCode": "60601",
          "County": null
        },
        "Salary": 99500,
        "Age": 45,
        "Manager": null,
        "Phones": {
          "$type": "System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib",
          "$values": [
            "312-555-0134",
            "312-555-0178"
          ]
        }
      },
      {
        "$type": "JsonQL.Demos.Examples.DataModels.Employee, JsonQL.Demos",
        "Id": 100000002,
        "FirstName": "Alice",
        "LastName": "Johnson",
        "Address": {
          "$type": "JsonQL.Demos.Examples.DataModels.Address, JsonQL.Demos",
          "Street": "123 Maple Street",
          "City": "New York",
          "State": "NY",
          "ZipCode": "10001",
          "County": null
        },
        "Salary": 105000,
        "Age": 38,
        "Manager": null,
        "Phones": {
          "$type": "System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib",
          "$values": [
            "212-555-0199"
          ]
        }
      },
      {
        "$type": "JsonQL.Demos.Examples.DataModels.Employee, JsonQL.Demos",
        "Id": 100000003,
        "FirstName": "Michael",
        "LastName": "Brown",
        "Address": {
          "$type": "JsonQL.Demos.Examples.DataModels.Address, JsonQL.Demos",
          "Street": "789 Pine Lane",
          "City": "Los Angeles",
          "State": "CA",
          "ZipCode": "90001",
          "County": null
        },
        "Salary": 89000,
        "Age": 50,
        "Manager": null,
        "Phones": {
          "$type": "System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib",
          "$values": []
        }
      },
      {
        "$type": "JsonQL.Demos.Examples.DataModels.Employee, JsonQL.Demos",
        "Id": 100000004,
        "FirstName": "Emily",
        "LastName": "Davis",
        "Address": {
          "$type": "JsonQL.Demos.Examples.DataModels.Address, JsonQL.Demos",
          "Street": "321 Elm Drive",
          "City": "Houston",
          "State": "TX",
          "ZipCode": "77001",
          "County": null
        },
        "Salary": 92000,
        "Age": 42,
        "Manager": null,
        "Phones": {
          "$type": "System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib",
          "$values": [
            "713-555-0147",
            "713-555-0112"
          ]
        }
      },
      {
        "$type": "JsonQL.Demos.Examples.DataModels.Employee, JsonQL.Demos",
        "Id": 100000005,
        "FirstName": "Christopher",
        "LastName": "Garcia",
        "Address": {
          "$type": "JsonQL.Demos.Examples.DataModels.Address, JsonQL.Demos",
          "Street": "654 Cedar Road",
          "City": "Phoenix",
          "State": "AZ",
          "ZipCode": "85001",
          "County": null
        },
        "Salary": 111000,
        "Age": 29,
        "Manager": null,
        "Phones": {
          "$type": "System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib",
          "$values": [
            "602-555-0166",
            "602-555-0188"
          ]
        }
      },
      {
        "$type": "JsonQL.Demos.Examples.DataModels.Employee, JsonQL.Demos",
        "Id": 100000007,
        "FirstName": "David",
        "LastName": "Martinez",
        "Address": {
          "$type": "JsonQL.Demos.Examples.DataModels.Address, JsonQL.Demos",
          "Street": "147 Birch Street",
          "City": "San Antonio",
          "State": "TX",
          "ZipCode": "78201",
          "County": null
        },
        "Salary": 95000,
        "Age": 46,
        "Manager": null,
        "Phones": {
          "$type": "System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib",
          "$values": [
            "210-555-0123"
          ]
        }
      },
      {
        "$type": "JsonQL.Demos.Examples.DataModels.Employee, JsonQL.Demos",
        "Id": 100000008,
        "FirstName": "Laura",
        "LastName": "Lee",
        "Address": {
          "$type": "JsonQL.Demos.Examples.DataModels.Address, JsonQL.Demos",
          "Street": "258 Willow Lane",
          "City": "San Diego",
          "State": "CA",
          "ZipCode": "92101",
          "County": null
        },
        "Salary": 105500,
        "Age": 32,
        "Manager": null,
        "Phones": {
          "$type": "System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib",
          "$values": [
            "619-555-0155",
            "619-555-0122"
          ]
        }
      },
      {
        "$type": "JsonQL.Demos.Examples.DataModels.Employee, JsonQL.Demos",
        "Id": 100000009,
        "FirstName": "Andrew",
        "LastName": "Harris",
        "Address": {
          "$type": "JsonQL.Demos.Examples.DataModels.Address, JsonQL.Demos",
          "Street": "369 Spruce Drive",
          "City": "Dallas",
          "State": "TX",
          "ZipCode": "75201",
          "County": null
        },
        "Salary": 88000,
        "Age": 41,
        "Manager": null,
        "Phones": {
          "$type": "System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib",
          "$values": [
            "214-555-0180"
          ]
        }
      },
      {
        "$type": "JsonQL.Demos.Examples.DataModels.Employee, JsonQL.Demos",
        "Id": 100000010,
        "FirstName": "Jessica",
        "LastName": "Thompson",
        "Address": {
          "$type": "JsonQL.Demos.Examples.DataModels.Address, JsonQL.Demos",
          "Street": "159 Cherry Lane",
          "City": "Austin",
          "State": "TX",
          "ZipCode": "73301",
          "County": null
        },
        "Salary": 98700,
        "Age": 37,
        "Manager": null,
        "Phones": {
          "$type": "System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib",
          "$values": []
        }
      },
      {
        "$type": "JsonQL.Demos.Examples.DataModels.Employee, JsonQL.Demos",
        "Id": 250150245,
        "FirstName": "Jane",
        "LastName": "Doe",
        "Address": {
          "$type": "JsonQL.Demos.Examples.DataModels.Address, JsonQL.Demos",
          "Street": "Main St",
          "City": "San Jose",
          "State": "PA",
          "ZipCode": "95101",
          "County": null
        },
        "Salary": 144186,
        "Age": 63,
        "Manager": null,
        "Phones": {
          "$type": "System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib",
          "$values": [
            "408-555-0133",
            "408-555-0190"
          ]
        }
      },
      {
        "$type": "JsonQL.Demos.Examples.DataModels.Employee, JsonQL.Demos",
        "Id": 783328759,
        "FirstName": "Robert",
        "LastName": "Brown",
        "Address": {
          "$type": "JsonQL.Demos.Examples.DataModels.Address, JsonQL.Demos",
          "Street": "Pine St",
          "City": "Los Angeles",
          "State": "CA",
          "ZipCode": "90001",
          "County": null
        },
        "Salary": 122395,
        "Age": 58,
        "Manager": null,
        "Phones": {
          "$type": "System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib",
          "$values": [
            "323-555-0177"
          ]
        }
      }
    ]
  },
  "ErrorsAndWarnings": {
    "$type": "JsonQL.Query.QueryResultErrorsAndWarnings, JsonQL",
    "CompilationErrors": {
      "$type": "JsonQL.Compilation.ICompilationErrorItem[], JsonQL",
      "$values": []
    },
    "ConversionErrors": {
      "$type": "JsonQL.JsonToObjectConversion.ConversionErrors, JsonQL",
      "Errors": {
        "$type": "System.Collections.Generic.List`1[[JsonQL.JsonToObjectConversion.IConversionError, JsonQL]], System.Private.CoreLib",
        "$values": []
      }
    },
    "ConversionWarnings": {
      "$type": "JsonQL.JsonToObjectConversion.ConversionErrors, JsonQL",
      "Errors": {
        "$type": "System.Collections.Generic.List`1[[JsonQL.JsonToObjectConversion.IConversionError, JsonQL]], System.Private.CoreLib",
        "$values": []
      }
    }
  }
}