Home > front end >  powershell, how to safely store variable type for future use
powershell, how to safely store variable type for future use

Time:12-23

I load some config from a JSON file and I want to be able to check what I've just loaded and potentially set some default values.

for now, I create a hash table containing info then load and check this way

$config_info=@{
    foo=@{default='' ;type=[string]}
    bar=@{default=@();type=[array] }
}

$config = Get-Content $configPath -Raw | ConvertFrom-Json

if(-not [Bool]$config.PSObject.Properties['foo']) {
    $config | Add-Member -MemberType NoteProperty -Name 'foo' -Value $config_info.foo.default 
}

if($config.foo -isnot $config_info.foo.type) { ... }

but I'm wondering how safe is the notation type=[string] and if I should rather use type=''.getType() witch looks less natural.

Update 1

in the past I used Export-Clixml/Import-Clixml for my config files but the resulting files are hardly human readable/ editable.

I also sometimes used dot-sourcing PS1 files but for the current project, config has to be maintain by non-powershell people.

What I'm wondering is, is type=[string] a safe/ lasting syntax to store ''.getType() in a variable, or not.

CodePudding user response:

What I'm wondering is, is type=[string] a safe/ lasting syntax to store ''.getType() in a variable, or not.

  • Yes, assuming that the .NET [string] type (System.String) isn't being shadowed by a custom class definition of the same name (which would be unwise to do).

  • [string] is a PowerShell type literal, and referring to a .NET type this way works predictably, assuming the type has been loaded into the session, which is by definition true for built-in types such as System.String. You can refer to a .NET type:

    • by its accelerator name, if defined; such as [string] or [regex]; use the .FullName property to see the type's full name; e.g. [regex].FullName

    • by its full name, though note that you're free to omit the System. part of the namespace, e.g., you can refer to System.Text.Encoding as [System.Text.Encoding] or [Text.Encoding]

    • by its assembly-qualified name; e.g. (obtained with [string].AssemblyQualifiedName):

      [System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]`
      
      • However:

        • This doesn't work across PowerShell editions, at least not for built-in types.

        • It is rarely necessary and can give the false impression that a given assembly version is being referenced (.NET seemingly loads whatever version is available)

        • However, in .NET (Core) / PowerShell (Core) only, you may omit the Version field; similarly, Culture and PublicKeyToken are optional.

        • While I doubt the need will arise, especially with respect to built-in types, at least hypothetically you can then disambiguate types with the same full name by their hosting assembly; e.g.:

           # PS Core (v7 ) only; the PublicKeyToken field may be omitted.
           [System.String, System.Private.CoreLib, PublicKeyToken=7cec85d7bea7798e]
          

A simpler PowerShell (Core) 7 solution:

  • In PowerShell (Core) 7 , ConvertFrom-Json has an -AsHashtable switch returns the parsing results as hashtables rather than as [pscustomobject] instances.

  • Given that you can cast from a hashtable to a custom class type literal, you can use strongly typed properties with default values that correspond to the properties in the input JSON data.

# Custom class that describes the structure of the config data,
# with strongly typed properties and default values.
class Config {
   [string] $foo = '(none)'
   [int[]] $bar = @()
}

# Sample JSON input; parse it into a hashtable.
$ht = @'
{
  "bar": [1, 2]
}
'@ | ConvertFrom-Json -AsHashtable

# Construct a [Config] instance from the values in the hashtable,
# enforcing data types, with on-demand conversion.
[Config] $ht

Output (note how foo has its default value):

foo    bar
---    ---
(none) {1, 2}

Note:

  • PowerShell's flexible automatic type conversions are used when constructing the Config instance, and given that PowerShell's allows any data type to be converted to [string], non-string input for [string]-typed properties is still accepted (resulting in stringification).

  • When a value is of the wrong type and cannot be converted (e.g., a value of "abc" for an [int]-typed property), the [Config] cast results in a statement-terminating error.

  • If you need more stringent type-checking, consider using validation by a JSON schema, which Test-Json supports via the -Schema and -SchemaFile parameters, in PowerShell (Core) only, as zett42 suggests.

  • Related