A REST service for validating Energy System Description Language (ESDL) files against user-defined validation schemas.
ESDL Validator allows you to define flexible validation rules (schemas) and run them against ESDL files via a REST API. It is designed to be extensible — new validation functions can be added easily using a plugin-based architecture.
Start the service with Docker Compose:
docker-compose up -dThis pulls the published image and starts the service with a MongoDB instance. The service will be available at http://localhost:3011. Navigate to the root URL to see the Swagger documentation.
- Upload a validation schema —
POST /schemawith a JSON schema definition (see Validation Schema below). - List available schemas —
GET /schemareturns a summary of all uploaded schemas. - Run a validation —
POST /validationToMessageswith an ESDL string and one or more schema IDs/names.
Swagger documentation is auto-generated and can be viewed by navigating to the root of the service. Note that the service does not include authentication/authorization, but can be handled in your own setup.
Manage validation schemas stored in the database.
| Endpoint | Method | Description |
|---|---|---|
/schema |
GET | Get a summary of schemas. Supports optional id and name query params for filtering. |
/schema |
POST | Upload a new validation schema |
/schema/{id_or_name} |
GET | Get a schema by ID or name |
/schema/{id_or_name} |
PUT | Update a schema by ID |
/schema/{id_or_name} |
DELETE | Delete a schema by ID |
Run validations and get results grouped per asset.
| Endpoint | Method | Description |
|---|---|---|
/validationToMessages |
POST | Validate an ESDL against one or more schemas. Returns messages grouped by asset ID. |
Request parameters:
data— ESDL file content as a stringschemas— Comma-separated list of schema IDs or names
Example response:
[
{
"assetID": "6f45c6f8-e8e2-4378-a910-45140337b9dd",
"messages": [
{
"severity": "ERROR",
"validation_message": "Required attribute not set or invalid",
"check_result_messages": ["[power] should satisfy > 0.0, but found [0.0]."]
}
]
}
]A validation schema defines a set of rules to validate an ESDL file. It consists of a name, description, and a list of validations.
{
"name": "My validation schema",
"description": "Schema to validate heat network assets",
"pre_validation_schemas": ["General checks"],
"post_validation_schemas": [],
"validations": [...]
}| Field | Required | Description |
|---|---|---|
name |
Yes | Name of the schema (must be unique) |
description |
Yes | Description of the schema |
pre_validation_schemas |
No | List of schema IDs or names to run before this schema |
post_validation_schemas |
No | List of schema IDs or names to run after this schema |
validations |
Yes | List of validation rules |
Each validation contains a name, description, severity type, a message, select functions, and a check function.
{
"name": "heatpump_required_attributes_are_set",
"description": "Report errors if the required attributes of HeatPump are not set.",
"type": "error",
"message": "Required attribute not set or invalid",
"selects": [
{
"function": "get",
"alias": "heatpumps",
"args": {
"type": ["HeatPump"]
}
}
],
"check": {
"function": "attributes_validation",
"dataset": "heatpumps",
"args": {
"null_checks": [
{ "attribute": "name", "count_as_null": [""] },
{ "attribute": "COP", "count_as_null": [0.0] }
],
"valid_checks": [
{
"attribute": "power",
"in_range": { "min_exclusive": 0.0 }
}
],
"resultMsgJSON": true
}
}
}| Field | Required | Description |
|---|---|---|
name |
Yes | Name of the validation rule |
description |
Yes | Description of the validation rule |
type |
Yes | Severity: "error" or "warning" |
message |
Yes | Message prefix for generated results |
selects |
Yes | List of select functions to generate datasets |
check |
Yes | Check function to run against the selected dataset |
There are two types of functions: select and check.
- Select functions generate a dataset from the ESDL. Multiple selects can be chained — each subsequent select can use the results of previous ones.
- Check functions test every entity in a dataset and return pass/fail results. Failed checks produce warnings or errors based on the validation's
typefield.
Functions are auto-discovered at startup. To reference a function in a schema, use its registered name (e.g., "function": "get").
Select all HeatPump assets with a filter on port count:
{
"function": "get",
"alias": "heatpumps",
"args": {
"type": ["HeatPump"],
"filter": [
{ "attribute": "port", "count": { "min": 4, "max": 4 } }
]
}
}This is the most commonly used check function. It supports null checks and validity/range checks on entity attributes or nested references.
{
"function": "attributes_validation",
"dataset": "assets",
"args": {
"null_checks": [
{ "attribute": "name", "count_as_null": [""] }
],
"valid_checks": [
{
"attribute": "efficiency",
"in_range": { "min_exclusive": 0.0, "max": 1.0 }
},
{
"attribute": "power",
"in_range": { "min_exclusive": 0.0 }
}
],
"resultMsgJSON": true
}
}It also supports checking attributes on nested references via the optional ref argument:
{
"function": "attributes_validation",
"dataset": "assets",
"args": {
"ref": {
"path": "costInformation.investmentCosts.profileQuantityAndUnit"
},
"null_checks": [],
"valid_checks": [
{ "attribute": "unit", "count_as_valid": "EURO" },
{ "attribute": "perUnit", "count_as_valid": "WATT" },
{ "attribute": "perMultiplier", "count_as_valid": ["MEGA", "KILO"] },
{ "attribute": "perTimeUnit", "count_as_valid": "Unset" }
],
"resultMsgJSON": true
}
}Used to compare attribute values between two reference paths on the same entity. For example, validating temperature relationships between ports.
{
"function": "compare_reference_attributes",
"dataset": "assets",
"args": {
"left": {
"ref": {
"path": "port",
"ref_list_filter": { "is_type": "OutPort", "match": { "name": "Out" } }
},
"attribute": "carrier.supplyTemperature"
},
"operator": "greater_than",
"right": {
"ref": {
"path": "port",
"ref_list_filter": { "is_type": "InPort", "match": { "name": "In" } }
},
"attribute": "carrier.returnTemperature"
},
"resultMsgJSON": true
}
}Supported operators: greater_than, less_than, equal.
For a full real-world example, see testdata/schemas/schema_NWN_general.json which validates a heat network topology including asset types, port configurations, carrier temperatures, and attribute requirements.
New functions can be added by creating a Python file in esdlvalidator/validation/functions/. The function will be auto-discovered at startup.
- Create a file in
esdlvalidator/validation/functions/, e.g.check_my_custom.py - Register the function with the
@FunctionFactory.registerdecorator - Inherit from
FunctionCheckand implementexecute()
from esdlvalidator.validation.functions.function import (
FunctionFactory, FunctionCheck, FunctionDefinition,
ArgDefinition, FunctionType, CheckResult,
)
from esdlvalidator.validation.functions import utils
@FunctionFactory.register(FunctionType.CHECK, "my_custom_check")
class MyCustomCheck(FunctionCheck):
def get_function_definition(self):
return FunctionDefinition(
"my_custom_check",
"Description of what this check does",
[ArgDefinition("my_arg", "Description of the argument", True)],
)
def execute(self):
entity = self.value # The current entity being checked
my_arg = self.args["my_arg"] # Arguments from the schema
# Your validation logic here
if some_condition_fails:
return CheckResult(False, "Error message")
return CheckResult(True)Same pattern, but inherit from FunctionSelect and register with FunctionType.SELECT:
from esdlvalidator.validation.functions.function import (
FunctionFactory, FunctionSelect, FunctionDefinition,
ArgDefinition, FunctionType,
)
@FunctionFactory.register(FunctionType.SELECT, "my_custom_select")
class MyCustomSelect(FunctionSelect):
def get_function_definition(self):
return FunctionDefinition(
"my_custom_select",
"Description of what this select does",
[ArgDefinition("my_arg", "Description of the argument", True)],
)
def execute(self):
dataset = self.datasets.get("resource")
# Your selection logic here
return selected_entitiesOnce added, reference the function by name in any validation schema: "function": "my_custom_check".
- Python >= 3.10
- uv — project and package manager
- MongoDB instance (for schema storage)
# Linux
curl -LsSf https://astral.sh/uv/install.sh | sh# Windows
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"uv syncThis installs all dependencies and automatically creates a .venv if one doesn't exist.
Copy .env.template to .env and adjust values as needed:
cp .env.template .envuv add <package> # Add a dependency
uv remove <package> # Remove a dependency
uv pip list # Check installed packagesUpdate the pyesdl version constraint in pyproject.toml and run uv sync.
ESDL Validator requires a MongoDB instance for schema storage. The host and port can be configured via MONGODB_HOST and MONGODB_PORT in .env.
uv run app.pyThe service starts on http://localhost:5000 by default.
uv run waitress-serve --listen="*:5000" --call "esdlvalidator.api.manage:create_app"To build and run from local source instead of the published image:
docker-compose up --buildTo build the image separately:
docker build -t esdl-validator .uv run pytestFor integrating ESDL Validator into the ESDL MapEditor toolsuite, see docker-compose-toolsuite.yml as a reference for connecting to the shared MapEditor network.
- Fix, update, and re-enable commented-out tests (
test_validator.py) - Improve test coverage for newer check functions (
compare_reference_attributes, etc.) - Clean up legacy schemas and select/check functions