Is an abstract class from which any custom process must extend. The functionality of this class is a bit advanced, but basically, it serves as a way to create functions to process the user input before or after the property tests.
This plugin uses processors as a way to allow the user to execute any custom code during the JSON validation process, in case the functionality provided by this framework is not enough. This page would explain how to use them. But first, let's discuss some behaviour of the validation process.
Each property execute the validation process in three phases:
- Preprocess: The user determines this phase behaviour. However, it can not modify the input data.
- Type validation: This phase executes all the validation tests that we have been discussing in other parts of this documentation.
- Postprocess: The user determines this phase behaviour. This phase can modify the input data.
Whenever an error arises in any of these phases, the process would not execute the following ones. On the other hand, raising warnings would not stop the validation process.
Also, when the process validates a dictionary or JSONPropertyObject, it first executes the preprocess, then its type validation consists on the preprocess, type validation, and postprocess cycle of each of the dictionary properties, and finally it executes the postprocess. The same occurs in JSONPropertyArray.
Finally, on a side note, the process validates the properties of a dictionary in the order they were defined. This fact becomes significant when the custom processes writte and read global variables.
Now we would explain what you can do when using custom processes:
- You can access the property to call its methods, using the 'get_property' method.
- You can raise custom errors and warnings, using the 'add_error' and 'add_warning' methods.
- You can modify the output of a certain property, but only while postprocessing.
- You can set global variables that can be accessed or modify at any point of the validation process, using the 'set_variable', 'has_variable' and 'get_variable' methods.
When creating a new processor, we would need to overwrite the '_preprocess' method, which does not receive the input data and does not return anything, or the '_postprocess' method, which gets the input data as a parameter and returns an output.
In this example, the JSON file would contain a 'min' and a 'max' property that would set the minimum and the maximum value of another property 'value':
{
"min": 0,
"max": 5,
"value": 2
}To achieve this, we would need three processors in three different scripts. First, the 'set_min' postprocessor:
extends JSONConfigProcessor
# Overwrite the '_postprocess' method
func _postprocess(minimum: int):
# Set a global variable called 'min'
set_variable("min", minimum)
# It is a postprocessor, so it must return a value
return minimumThe 'set_max' postprocessor:
extends JSONConfigProcessor
# Overwrite the '_postprocess' method
func _postprocess(maximum: int):
# Set a global variable called 'max'
set_variable("max", maximum)
# It is a postprocessor, so it must return a value
return maximumThe 'set_range' preprocessor. It is crucial to notice that we need to check if the variables exists. Just in case the input data is non-valid, and the variables are not declared:
extends JSONConfigProcessor
# Overwrite the '_preprocess' method
func _preprocess():
# Check if the global variable min exists
if has_variable("min"):
# Set the integer minimum value
get_property().set_min_value(get_variable("min"))
# Check if the global variable max exists
if has_variable("max"):
# Set the integer maximum value
get_property().set_max_value(get_variable("max"))
# It is a preprocessor, so it does not return a valueNow we can declare a new JSON configuration file that would check if 'value' is between 'min' and 'max':
# Create the 'min' property
var min_value = JSONPropertyInteger.new()
min_value.set_postprocessor(preload("res://set_min.gd").new())
# Create the 'max' property
var max_value = JSONPropertyInteger.new()
max_value.set_postprocessor(preload("res://set_max.gd").new())
# Create the 'value' property
var value = JSONPropertyInteger.new()
value.set_preprocessor(preload("res://set_range.gd").new())
# Create the JSON configuration file
var json_config_file = JSONConfigFile.new()
# Add the 'min' property
json_config_file.add_property("min", min_value)
# Add the 'max' property
json_config_file.add_property("max", max_value)
# Add the 'value' property
json_config_file.add_property("value", value)
# Validate input
json_config_file.validate(json_file_path)In this case, we want the user to introduce an enum. This field would be a string on the final dictionary, but we can transform this field to be an integer that represents a value of a Godot's enum. For this purpose, we must define a postprocessor called 'transform_gender_enum' that looks like this:
extends JSONConfigProcessor
enum Genders{
MALE,
FEMALE,
NON_BINARY
}
func _postprocess(gender: String):
match gender:
"MALE":
return Genders.MALE
"FEMALE":
return Genders.FEMALE
"NON_BINARY":
return Genders.NON_BINARY
return -1Then we can create the following JSON configuration file:
# Create the 'gender' property
var gender = JSONPropertyEnum.new()
gender.set_enum(["MALE", "FEMALE", "NON_BINARY"])
gender.set_postprocessor(preload("res://transform_gender_enum.gd").new())
# Create the JSON configuration file
var json_config_file = JSONConfigFile.new()
# Add the 'gender' property
json_config_file.add_property("gender", gender)
# Validate input
json_config_file.validate(json_file_path)Using this method, this JSON file:
{
"gender": "MALE"
}Would generate this dictionary:
{
"gender": Genders.MALE
}In this last example, we would raise a custom error. We will check if an integer is prime. For this purpose, we need to create another postprocessor called 'prime_check':
extends JSONConfigProcessor
func _postprocess(integer: int):
# One is not a prime >:(
if integer == 1:
add_error({"error": "This is not a prime"})
return null
# A simple prime check
for i in range(2, sqrt(integer) + 1):
# If is not prime
if integer % i == 0:
add_error({"error": "This is not a prime"})
return null
return integerNow we can create the following JSON configuration file:
# Create the 'prime' property
var prime = JSONPropertyInteger.new()
prime.set_postprocessor(preload("res://prime_check.gd").new())
# Create the JSON configuration file
var json_config_file = JSONConfigFile.new()
# Add the 'prime' property
json_config_file.add_property("prime", prime)
# Validate input
json_config_file.validate(json_file_path)In this case, this JSON file would not raise any error:
{
"prime": 7
}This file, on the other hand, would raise our custom error:
{
"prime": 4
}Returned error:
[
{
"error": "This is not a prime",
"context": "prime",
"as_text": "This is not a prime, at 'prime'."
}
]The public methods of this class are:
| Name | Params | Description | Returns |
|---|---|---|---|
| add_error | error -> Dictionary: Custom error. |
Raises a custom error, if you want it to have the 'as_text' property, include your custom error message in an 'error' property. | Nothing. |
| add_warning | warning -> Dictionary: Custom warning. |
Raises a custom warning, if you want it to have the 'as_text' property, include your custom warning message in an 'warning' property. | Nothing. |
| set_variable | name -> String: Name of the global variable to set. value -> Variant: The value assigned to this global variable. |
Creates a global variable or modify the value of a previously existing one. | Nothing. |
| has_variable | name -> String: Name of the global variable to check. |
Checks if the global variable exists. | bool: If the global variable exists. |
| get_variable | name -> String: Name of the global variable to get. |
Gets the value of the specified global variable. | Variant: The value of the global variable. If the variable is undefined, it will return null. |
| get_property | None. | Returns the JSON property in which this processor is located. | JSONProperty: The JSON property in which this processor is located. |