Hide navigation sidebar
Hide table of contents sidebar
Skip to content
Back to top
View this page
Toggle table of contents sidebar

# Validating `package.json`

marshmallow can be used to validate configuration according to a schema. Below is a schema that could be used to validate `package.json` files. This example demonstrates the following features:

- Validation and deserialization using `Schema.load`

- Custom fields

- Specifying deserialization keys using `data_key`

- Including unknown keys using `unknown = INCLUDE`

    # /// script
    # requires-python = ">=3.9"
    # dependencies = [
    #     "marshmallow",
    #     "packaging>=17.0",
    # ]
    # ///
    import json
    import sys
    from pprint import pprint

    from packaging import version

    from marshmallow import INCLUDE, Schema, ValidationError, fields

    class Version(fields.Field):
        """Version field that deserializes to a Version object."""

        def _deserialize(self, value, *args, **kwargs):
            try:
                return version.Version(value)
            except version.InvalidVersion as e:
                raise ValidationError("Not a valid version.") from e

        def _serialize(self, value, *args, **kwargs):
            return str(value)

    class PackageSchema(Schema):
        name = fields.Str(required=True)
        version = Version(required=True)
        description = fields.Str(required=True)
        main = fields.Str(required=False)
        homepage = fields.URL(required=False)
        scripts = fields.Dict(keys=fields.Str(), values=fields.Str())
        license = fields.Str(required=True)
        dependencies = fields.Dict(keys=fields.Str(), values=fields.Str(), required=False)
        dev_dependencies = fields.Dict(
            keys=fields.Str(),
            values=fields.Str(),
            required=False,
            data_key="devDependencies",
        )

        class Meta:
            # Include unknown fields in the deserialized output
            unknown = INCLUDE

    if __name__ == "__main__":
        pkg = json.load(sys.stdin)
        try:
            pprint(PackageSchema().load(pkg))
        except ValidationError as error:
            print("ERROR: package.json is invalid")
            pprint(error.messages)
            sys.exit(1)

Given the following `package.json` file…

    {
      "name": "dunderscore",
      "version": "1.2.3",
      "description": "The Pythonic JavaScript toolkit",
      "devDependencies": {
        "pest": "^23.4.1"
      },
      "main": "index.js",
      "scripts": {
        "test": "pest"
      },
      "license": "MIT"
    }

We can validate it using the above script.

    $ uv run examples/package_json_example.py < examples/package.json
    {'description': 'The Pythonic JavaScript toolkit',
    'dev_dependencies': {'pest': '^23.4.1'},
    'license': 'MIT',
    'main': 'index.js',
    'name': 'dunderscore',
    'scripts': {'test': 'pest'},
    'version': <Version('1.2.3')>}

Notice that our custom field deserialized the version string to a `Version` object.

But if we pass an invalid package.json file…

    {
      "name": "dunderscore",
      "version": "INVALID",
      "homepage": "INVALID",
      "description": "The Pythonic JavaScript toolkit",
      "license": "MIT"
    }

We see the corresponding error messages.

    $ uv run examples/package_json_example.py < examples/invalid_package.json
    ERROR: package.json is invalid
    {'homepage': ['Not a valid URL.'], 'version': ['Not a valid version.']}