如何在PowerShell中将字符串值转换为动态数据类型?

时间:2019-04-24 00:21:27

标签: powershell dynamic types casting

如果事先不知道数据类型,是否可以将字符串值分配给其他类型的变量?例如,在下面的示例中,如何在不更改其数据类型的情况下更新$values哈希值:

$values = @{
    "Boolean" = $true
    "Int"     = 5
    "DateTime"= (Get-Date)
    "Array"   = @("A", "B", "C")
}

$stringValues = @{
    "Boolean" = 'false'
    "Int"     = '10'
    "DateTime"= '2019-01-02 14:45:59.146'
    "Array"   = '@("X", "Y", "Z")'
}

"INITIAL VALUES:"
foreach ($key in $values.Keys) {
    ($key + " = " + $values[$key] + " (" + $values[$key].GetType().FullName + ")")
}

"`nUPDATING..."
foreach ($key in $stringValues.Keys) {
    $values[$key] = $stringValues[$key]
}

"`nUPDATED VALUES:"
foreach ($key in $values.Keys) {
    ($key + " = " + $values[$key] + " (" + $values[$key].GetType().FullName + ")")
}

输出:

INITIAL VALUES:
DateTime = 04/23/2019 16:54:13 (System.DateTime)
Array = A B C (System.Object[])
Boolean = True (System.Boolean)
Int = 5 (System.Int32)

UPDATING...

UPDATED VALUES:
DateTime = 2019-01-02 14:45:59.146 (System.String)
Array = @("X", "Y", "Z") (System.String)
Boolean = false (System.String)
Int = 10 (System.String)

我需要更新后的值以匹配原始数据类型,而不仅仅是转换为System.String

我对字符串的内容很灵活。例如。包含布尔值false的字符串可以是$false / false / [boolean]false / [boolean]$false / etc或包含数组的字符串可以使用不同的格式(基本上,更容易将字符串转换为适当的数据类型。

本质上,我想模拟ConvertFrom-Json cmdlet从JSON字符串设置对象属性时所做的一切,仅就我而言,我没有JSON结构。

(以防万一有人想知道我要做什么:我正在尝试向我的ConfigFile module添加一个INI文件解析器,不,我不能仅使用哈希返回INI设置;我需要要将值加载到相应的PSVariable中,并且要使其正常工作,我需要将字符串转换为适当的数据类型。)

4 个答案:

答案 0 :(得分:2)

您可以使用自定义类代替哈希表;与哈希表键不同,自定义类的属性可以专门键入。

  • 使用标量值,然后您可以简单地让PowerShell为您执行从字符串转换-除了布尔字符串需要特殊处理(请参见下面的源代码中的注释)。

  • 使用数组,事情变得更加棘手。下面的解决方案使用[System.Management.Automation.PSParser]::Tokenize()来解析字符串,但目前仅限于识别字符串和数字文字。

    • 注意:在整个阵列上使用Invoke-Expression是很诱人的,但这将带来安全风险,因为它为任意代码执行打开了方便之门。尽管存在合法用途-例如在已知表示以下数字的字符串上-Invoke-Expression should generally be avoided

(如果不想定义类,则可以从哈希表$values values 派生类型,并使用[System.Management.Automation.LanguagePrimitives]::ConvertTo()将字符串转换为这些类型,如LotPings' answer所示,但请注意,数组和布尔值仍需要进行特殊处理,如下所示。)

# Define a custom [Values] class
# with specifically typed properties.
class Values {
  [bool]     $Boolean
  [int]      $Int
  [datetime] $DateTime
  [Array]    $Array
}

# Instantiate a [Values] instance
$values = [Values] @{
  Boolean = $true
  Int     = 5
  DateTime= (Get-Date)
  Array   = @("A", "B", "C")
}

$stringValues = @{
  Boolean = 'false'
  Int     = '10'
  DateTime = '2019-01-02 14:45:59.146'
  Array   = '@("X", "Y", "Z")'
}

"INITIAL VALUES:"
foreach ($key in $values.psobject.properties.Name) {
  ($key + " = " + $values.$key + " (" + $values.$key.GetType().FullName + ")")
}

""
"UPDATING..."

foreach ($key in $stringValues.Keys) {
  switch ($key) {
    'Array' {
      # Parse the string representation.
      # Assumptions and limitations:
      #  The array is flat.
      #  It is sufficient to only support string and numeric constants.
      #  No true syntax validation is needed.
      $values.$key = switch ([System.Management.Automation.PSParser]::Tokenize($stringValues[$key], [ref] $null).Where( { $_.Type -in 'String', 'Number' })) {
        { $_.Type -eq 'String' } { $_.Content; continue }
        { $_.Type -eq 'Number' } { Invoke-Expression $_Content; continue }
      }
      continue
    }
    'Boolean' {  # Boolean scalar
      # Boolean strings need special treatment, because PowerShell considers
      # any nonempty string $true
      $values.$key = $stringValues[$key] -notin 'false', '$false'
      continue
    }
    default { # Non-Boolean scalar
      # Let PowerShell perform automatic from-string conversion
      # based on the type of the [Values] class' target property.
      $values.$key = $stringValues[$key]
    }
  }
}

""
"UPDATED VALUES:"
foreach ($key in $values.psobject.properties.Name) {
  ($key + " = " + $values.$key + " (" + $values.$key.GetType().FullName + ")")
}

这将产生:

INITIAL VALUES:
Boolean = True (System.Boolean)
Int = 5 (System.Int32)
DateTime = 04/24/2019 14:45:29 (System.DateTime)
Array = A B C (System.Object[])

UPDATING...

UPDATED VALUES:
Boolean = True (System.Boolean)
Int = 10 (System.Int32)
DateTime = 01/02/2019 14:45:59 (System.DateTime)
Array = X Y Z (System.Object[])

答案 1 :(得分:1)

在Write-Host上达成协议。实际上,它仅应用于利用颜色输出和某些特定格式的情况。输出到屏幕是您在响应中看到的默认设置。

您可以执行以下操作,但是日期字符串有点奇怪,对我而言,我还没有看到任何人使用该格式。因此,我将其修改为美国风格,但根据您的语言进行更改。

$values = @{
    'Boolean' = $true
    'Int'     = 5
    'DateTime'= (Get-Date)
    'Array'   = @('A', 'B', 'C')
}

$stringValues = @{
    'Boolean' = 'false'
    'Int'     = '10'
    'DateTime'= '2019-01-02 14:45:59'
    'Array'   = "@('X', 'Y', 'Z')"
}

'INITIAL VALUES:'
foreach ($key in $values.Keys) 
{
    "$key = $($values[$key]) $($values[$key].GetType())"
}


"`nUPDATING..."
foreach ($key in $stringValues.Keys) 
{
    switch ($key) 
    { 
        Boolean  {[Boolean]$values[$key] = $stringValues['$'+$key]} 
        Int      {[Int]$values[$key] = $stringValues[$key]} 
        DateTime {[DateTime]$values[$key] = $stringValues[$key]} 
        Array    {[Array]$values[$key] = $stringValues[$key]} 
        default {'The value could not be determined.'}
    }
}


"`nUPDATED VALUES:"
foreach ($key in $values.Keys) 
{
    "$key = $($values[$key]) $($values[$key].GetType())"
}

# Results

INITIAL VALUES:
DateTime = 04/24/2019 01:44:17 datetime
Array = A B C System.Object[]
Boolean = True bool
Int = 5 int

UPDATING...

UPDATED VALUES:
DateTime = 01/02/2019 14:45:59 datetime
Array = @("X", "Y", "Z") System.Object[]
Boolean = False bool
Int = 10 int

答案 2 :(得分:1)

因此您想将新值转换/转换为旧值的类型。

这个想法需要从变量中转换出来,
这是一个相关的问题powershell-type-cast-using-type-stored-in-variable

答案提示:

  

您可以使用以下方法大致模拟演员表:   [System.Management.Automation.LanguagePrimitives] :: ConvertTo($ Value,$ TargetType)

以下更改后的例程显示:并不是那么简单,尤其是当新数据在转换中需要重载/其他参数时。

"UPDATING..."
foreach ($key in $stringValues.Keys) {
    $values[$key] = [System.Management.Automation.LanguagePrimitives]::ConvertTo(
                    $stringValues[$key], $values[$key].gettype())
}

我的德语语言环境错误消息:

  

Ausnahme beim Aufrufen von“ ConvertTo” mit 2自变量:“ Der Wert” 2019-01-02 14:45.59.146“ kann nicht in den Typ   “ System.DateTime”显示广告。费勒:“死去的日期时间不正确。”   在Zeile:2 Zeichen:5   + $ values [$ key] = [System.Management.Automation.LanguagePrimitives] ...   + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~       + CategoryInfo:未指定:(:) [],MethodInvocationException       + FullyQualifiedErrorId:PSInvalidCastException

结果不足:

DateTime = 04/24/2019 09:49:19 (System.DateTime)
Array = @("X", "Y", "Z") (System.Object[])
Boolean = True (System.Boolean)
Int = 10 (System.Int32)

您可以详细说明这个想法,更单独地处理旧类型/新数据。

答案 3 :(得分:0)

感谢@ LotPings,@ mklement0和@postanote给我一些想法,所以这里是我要使用的解决方案:

$values = @{
    "Boolean" = $true
    "Int"     = 5
    "DateTime"= (Get-Date)
    "Array"   = @("A", "B", "C")
}

$stringValues = @{
    "Boolean" = 'false'
    "Int"     = '10'
    "DateTime"= '2019-01-31 14:45:59.005'
    "Array"   = 'X,Y,Z'
}

"INITIAL VALUES:"
foreach ($key in $values.Keys) {
    ($key + " = " + $values[$key] + " (" + $values[$key].GetType().FullName + ")")
}

"`nUPDATING..."
foreach ($key in $stringValues.Keys) {
    $value = $stringValues[$key]

    if ($values[$key] -is [Array]) {
        $values[$key] = $value -split ','
    }
    elseif (($values[$key] -is [Boolean]) -or ($values[$key] -is [Switch])) {
        $values[$key] = $value -notin 'false', '$false', '0', ''
    }
    else {
        $values[$key] = [System.Management.Automation.LanguagePrimitives]::ConvertTo($value, $values[$key].GetType())
    }
}

"`nUPDATED VALUES:"
foreach ($key in $values.Keys) {
    ($key + " = " + $values[$key] + " (" + $values[$key].GetType().FullName + ")")
}

输出:

INITIAL VALUES:
DateTime = 04/25/2019 09:32:31 (System.DateTime)
Array = A B C (System.Object[])
Boolean = True (System.Boolean)
Int = 5 (System.Int32)

UPDATING...

UPDATED VALUES:
DateTime = 01/31/2019 14:45:59 (System.DateTime)
Array = X Y Z (System.String[])
Boolean = False (System.Boolean)
Int = 10 (System.Int32)

我调整了字符串值中的数组格式(正如我在问题中提到的那样,这是一个选择)。使用此代码的实际代码将有所不同,但是基本思想在这里。我注意到的唯一警告是,数组数据类型从object[]更改为string[]。理想情况下,我希望保持原样,但它不会更改代码的功能,因此很好。再次感谢所有人的想法和纠正,如果您提出更好的选择,请随时发帖。