Conversion Settings

  • Conversion settings control how JSON query result generated by call to one of the overloaded JsonQL.Query.IQueryManager.QueryObject<TQueriedObject>(…), (e.g., queryManager.QueryObject<IEmployee?[]>(…)) is converted to expected type instance.

  • This includes things like errors reported (or ignored) during conversion process or the expected format of JSON object keys (e.g., Pascal or camel case format for field names).

  • Conversion settings are setup once by configuring an instance of JsonQL.JsonToObjectConversion.IJsonConversionSettings that is injected into JsonQL.Query.QueryManager (normally done as part of dependency injection setup on application start).

  • All conversion settings in JsonQL.JsonToObjectConversion.IJsonConversionSettings can be overridden using optional parameter jsonConversionSettingOverrides of type JsonQL.JsonToObjectConversion.IJsonConversionSettingsOverrides used in calls to one of the overloaded JsonQL.Query.IQueryManager.QueryObject<TQueriedObject>(…).

Code snippet example demonstrating configuring JsonQL.JsonToObjectConversion.IJsonConversionSettings.

private static IJsonConversionSettings CreateConversionSettings()
{
    var conversionErrorTypeConfigurations = new List<ConversionErrorTypeConfiguration>();

    foreach (var conversionErrorType in Enum.GetValues<ConversionErrorType>())
    {
        // Set custom ErrorReportingType for ConversionErrorType here.
        // We report all errors as ErrorReportingType.ReportAsError by default.
        conversionErrorTypeConfigurations.Add(new ConversionErrorTypeConfiguration(conversionErrorType, ErrorReportingType.ReportAsError));
    }

    return new JsonConversionSettings
    {
        JsonPropertyFormat = JsonPropertyFormat.PascalCase,
        FailOnFirstError = true,
        ConversionErrorTypeConfigurations = conversionErrorTypeConfigurations,

        // Set custom interface to implementation mappings here.
        // Default mapping mechanism (i.e., IModelClassMapper) will
        // try to find an implementation that has the same name space and class
        // name that matches interface name without "I" prefix (if the mapped
        // type defaultTypeToConvertParsedJsonTo is an interface).
        // For example for interface JsonQL.Demos.Examples.DataModels.IEmployee class
        // JsonQL.Demos.Examples.DataModels.Employee will be used if it exists and it
        // implements JsonQL.Demos.Examples.DataModels.IEmployee.
        // If defaultTypeToConvertParsedJsonTo is a class, the default mapping mechanism will
        // use the class itself.
        TryMapJsonConversionType =
                (defaultTypeToConvertParsedJsonTo, convertedParsedJson) =>
        {
            if (defaultTypeToConvertParsedJsonTo == typeof(IEmployee))
            {
                if (convertedParsedJson.HasKey(nameof(IManager.Employees)))
                    return typeof(IManager);

                if (!convertedParsedJson.HasKey(nameof(Employee.Id)))
                    return typeof(EmployeeWithoutId);
            }

            // Returning null will result default mapping mechanism picking a type to use.
            return null;
        }
    };
}

Code snippet example demonstrating overriding some settings configured in JsonQL.JsonToObjectConversion.IJsonConversionSettings for a specific query execution using optional parameter jsonConversionSettingOverrides of type JsonQL.JsonToObjectConversion.IJsonConversionSettingsOverrides.

// 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!;

var employeesResult = queryManager.QueryObject<IReadOnlyList<IEmployee>>(query,
    new JsonTextData("Companies",
        LoadJsonFileHelpers.LoadJsonFile("Companies.json",
        ["DocFiles", "QueryingJsonFiles", "JsonFiles"])),
    jsonConversionSettingOverrides: new JsonConversionSettingsOverrides
    {
        // Lets report nullable values not set errors as warnings. The other errors not specified in
        // ConversionErrorTypeConfigurations will use the values set in
        // JsonQL.JsonToObjectConversion.IJsonConversionSettings
        ConversionErrorTypeConfigurations = new List<IConversionErrorTypeConfiguration>
        {
            new ConversionErrorTypeConfiguration(ConversionErrorType.NonNullablePropertyNotSet, ErrorReportingType.ReportAsWarning),
            new ConversionErrorTypeConfiguration(ConversionErrorType.NonNullablePropertyNotSet, ErrorReportingType.ReportAsWarning),
            new ConversionErrorTypeConfiguration(ConversionErrorType.ValueNotSet, ErrorReportingType.ReportAsWarning)
        },
        FailOnFirstError = false,
        TryMapJsonConversionType = (defaultTypeToConvertParsedJsonTo, convertedParsedJson) =>
        {
            if (defaultTypeToConvertParsedJsonTo == typeof(Employee) &&
                convertedParsedJson.HasKey(nameof(IManager.Employees)) &&
                !(convertedParsedJson.TryGetJsonKeyValue(nameof(IManager.Employees), out var employees) &&
                  employees.Value is IParsedArrayValue employeesArray &&
                  employeesArray.Values.Count > 0))
            {
                return typeof(ManagerWithoutEmployees);
            }

            // Returning null will result in configuration setup in
            // JsonQL.JsonToObjectConversion.IJsonConversionSettings being used.
            return null;
        }
    });