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

问题描述 投票:1回答:4

如果事先不知道数据类型,是否可以将字符串值分配给不同类型的变量?例如,在下面的示例中,如何在不更改其数据类型的情况下更新$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 + ")")
}

OUTPUT:

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结构。

(如果有人想知道我正在尝试做什么:我试图将一个INI文件解析器添加到我的ConfigFile module,不,我不能只使用哈希来返回INI设置;我需要将值加载到相应的PSVariables并为此工作,我需要将字符串转换为正确的数据类型。)

powershell dynamic types casting
4个回答
2
投票

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

  • 使用标量值,您可以简单地让PowerShell为您执行from-string转换 - 除了布尔字符串需要特殊处理(请参阅下面的源代码中的注释)。
  • 对于数组,事情比较棘手。下面的解决方案使用[System.Management.Automation.PSParser]::Tokenize()来解析字符串,但目前仅限于识别字符串和数字文字。 注意:在整个阵列上使用Invoke-Expression很诱人,但这会带来安全风险,因为它打开了通向任意代码的大门。虽然有合法的用途 - 例如在已知代表下面数字的字符串上 - Invoke-Expression should generally be avoided

(如果您不想定义类,可以从散列表$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
投票

因此,您希望将新值转换/转换为旧值的类型。

这个想法需要从变量中转换出来, 这是一个相关的问题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())
}

我的德语区域设置错误消息:

使用2个参数调用“ConvertTo”时出现异常:“值”2019-01-02 14:45.59.146“无法转换为”System.DateTime“类型错误:”该字符串未用作有效的DateTime被识别。“”在行:2个字符:5 + $ values [$ key] = [System.Management.Automation.LanguagePrimitives] ... + ~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CategoryInfo:NotSpecified:(:) [],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)

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


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

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 + ")")
}

OUTPUT:

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[]。理想情况下,我希望保持原样,但它不会改变代码的功能,所以很好。再次感谢所有想法和更正,如果你想出更好的选择,请随时发布。

© www.soinside.com 2019 - 2024. All rights reserved.