Behind the PowerShell Pipeline logo

Behind the PowerShell Pipeline

Subscribe
Archives
May 9, 2025

More Scheming with JSON

Last time we started looking into creating JSON schemas. This is a great way to validate JSON data and ensure the file is in the correct format with the correct data. Let's continue by looking at a few more advanced features of JSON schemas.

In last month's wrap-up, I mentioned a PowerShell tool I wrote to make it easy to import local modules into my session. The code relies on a JSON file to store the module information.

[
  {
    "ModuleTitle": "Stripe Tools",
    "ModuleName": "StripeTools",
    "Path": "c:\\scripts\\StripeTools\\StripeTools.psd1"
  },
  {
    "ModuleTitle": "MuseScore Tools",
    "ModuleName": "MuseScoreTools",
    "Path": "c:\\scripts\\MuseScoreTools\\MuseScoreTools.psd1"
  }
]

With this as a guide, I created a corresponding schema.

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "PowerShell Local Modules Configuration",
  "description": "Schema for listing PowerShell modules with their titles, names, and file paths",
  "type": "array",
  "items": {
    "type": "object",
    "required": [
      "ModuleTitle",
      "ModuleName",
      "Path"
    ],
    "properties": {
      "ModuleTitle": {
        "type": "string",
        "description": "Display name or title of the PowerShell module"
      },
      "ModuleName": {
        "type": "string",
        "description": "Technical name of the PowerShell module"
      },
      "Path": {
        "type": "string",
        "description": "File system path to the module manifest (.psd1) or script module (.psm1) file",
        "pattern": "^[a-zA-Z]:\\\\.*\\.(psd1|psm1)$"
      }
    }
  }
}

The schema defines a set of required properties for each item in the array. One new element is pattern. I can use a regular expression pattern to validate value. I'll intentionally break the JSON file to show the validation in action.

PS C:\scripts> Test-Json -Path .\localmodules.json -SchemaFile .\localmodules.schema.json
Test-Json: The JSON is not valid with the schema: The string value is not a match for the indicated regular expression at '/0/Path'
False

That helps. However, there is a challenge with this file. Because the localmodules.json file is an array, I can't insert a $schema property to reference the schema. This means VS Code can't provide real-time validation. In order to add the property, I need to revise localmodules.json to be an object. I can do this by wrapping the array in an object with a property name of ModuleInfo.

{
  "$schema": "file:///c:/scripts/localmodules2.schema.json",
  "ModuleInfo": [
    {
      "ModuleTitle": "Stripe Tools",
      "ModuleName": "StripeTools",
      "Path": "c:\\scripts\\StripeTools\\StripeTools.psd1"
    },
    {
      "ModuleTitle": "MuseScore Tools",
      "ModuleName": "MuseScoreTools",
      "Path": "c:\\scripts\\MuseScoreTools\\MuseScoreTools.psd1"
    },
    {
      "ModuleTitle": "Pluralsight Tools",
      "ModuleName": "PluralsightTools",
      "Path": "c:\\scripts\\PluralsightTools\\PluralsightTools.psd1"
    }
  ]
}

This is a truncated version of the actual file. Now I can insert the schema reference. Here's the revised schema.

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "PowerShell Local Modules Configuration",
  "description": "Schema for listing PowerShell modules with their titles, names, and file paths",
  "type": "object",
  "ModuleInfo": {
    "type": "array",
    "description": "Local module details",
    "items": {
      "type": "object",
      "properties": {
        "ModuleTitle": {
          "type": "string",
          "description": "Display name or title of the PowerShell module"
        },
        "ModuleName": {
          "type": "string",
          "description": "Technical name of the PowerShell module"
        },
        "Path": {
          "type": "string",
          "description": "File system path to the module manifest (.psd1) or script module (.psm1) file",
          "pattern": "^[a-zA-Z]:\\\\.*\\.(psd1|psm1)$"
        }
      },
      "required": [
        "ModuleTitle",
        "ModuleName",
        "Path"
      ]
    }
  }
}

Note the hierarchy of types. The top-level type is an object. This object has a property of ModuleInfo. This object is an array of nested objects. Each nested item has its own set of defined and required properties.

> The new JSON layout will require me to update the code in the Import-LocalModule function to reference the new ModuleInfo property. While I'm at it, I'm also going to make all property names lower case to avoid any casing problems.

Additional Properties

There is one important caveat regarding JSON schemas that I've shown you to this point. They only validate the JSON against the schema. If the JSON file has other properties, they are ignored. Here's a sample JSON file.

{
  "$schema": "file:///c:/scripts/demo.schema.json",
  "Name": "Jeff",
  "Version": 7.5,
  "PSHome": "C:\\Program Files\\PowerShell\\7",
  "Services": [
    "WinRm",
    "WinMgmt",
    "WSearch"
  ],
  "Comment": "This is demo data.",
  "foo": "bar"
}

The file passes testing this against the schema.

PS C:\> Test-Json -path c:\scripts\demo2.json -SchemaFile C:\scripts\demo.schema.json
True

Even though the foo property is not defined in the schema. If you want to ensure that the JSON file only contains the properties defined in the schema, you can set additionalProperties to false.

Want to read the full issue?
GitHub Bluesky LinkedIn About Jeff
Powered by Buttondown, the easiest way to start and grow your newsletter.