使用Powershell解析具有变量类型的XML文件

使用Powershell解析具有变量类型的XML文件,xml,powershell,Xml,Powershell,我试图从脚本中删除变量的定义,并从XML配置文件中读取它们,如下所示: [xml]$configFile = Get-Content $PSScriptRoot\$confFile $settings = $configFile.settings.ChildNodes foreach ($setting in $settings) { New-Variable -Name $setting.LocalName -Value ($setting.InnerText -replace '

我试图从脚本中删除变量的定义,并从XML配置文件中读取它们,如下所示:

[xml]$configFile = Get-Content $PSScriptRoot\$confFile
$settings = $configFile.settings.ChildNodes
foreach ($setting in $settings) {  
    New-Variable -Name $setting.LocalName -Value ($setting.InnerText -replace '##DATE##',(get-date -f yyyy-MM-dd)) -Force
}
XML文件

<?xml version="1.0" encoding="utf-8" ?>
<settings>
    <process>FALSE</process>
    <xmlDir>\\serv1\dev</xmlDir>
    <scanDir>\\serv1\dev</scanDir>
    <processedDir>\\serv1\dev\done</processedDir>
    <errorDir>\\serv1\dev\err</errorDir>
    <log>\\serv1\dev\log\dev-Log##DATE##.log</log>
    <retryDelay>5</retryDelay>
    <retryLimit>3</retryLimit>
</settings>
这很好,但问题是它们都是作为字符串读取的,但我需要一些作为整数。为了解决这个问题,我必须在创建变量后将其更改为整数,如下所示:

$retryDelay = ([int]$retryDelay)
$retryLimit = ([int]$retryLimit)

虽然这是可行的,但我希望XML中有其他变量,如布尔$true/$false(并以布尔形式读入),并且希望foreach能够处理它们的类型,而不是脚本中的其他行。任何线索都值得欣赏。

首先,不要像这样阅读XML文件。这破坏了XML解析器中内置的编码检测,迟早会导致数据损坏

# BAD, DO NOT USE
[xml]$configFile = Get-Content $PSScriptRoot\$confFile
正确读取XML文件的工作原理如下-创建一个新的XML对象并让它处理文件加载:

$configFile = New-Object xml
$configFile.Load("$PSScriptRoot\$confFile")
其次,我强烈建议不要从文件中创建全局变量。这是一种糟糕的风格,因为它可以通过盲目地覆盖现有变量而轻易破坏程序。使用散列存储文件中的值,或者直接使用XML文件作为配置文件

$config = @{}

foreach ($setting in $configFile.SelectNodes("/settings/*") ) {
    $config[$setting.Name] = $setting.InnerText
}
第三,XML没有固有的数据类型信息。在你添加更多信息之前,一切都是一个字符串。一种方法可以是
type
属性(
type=“string”
可以视为默认值):

使用
convertfromjson
cmdlet解析数据。使用
获取内容-编码UTF8
读取它

在处理文本文件时,使用
编码
参数很重要,在使用
设置内容
输出文件
编写文件时也很重要。这里没有做正确事情的隐藏魔法,您必须明确编码


下面是有关
Out File
Set Content
行为的一些更深入的信息

首先,不要像这样读取XML文件。这破坏了XML解析器中内置的编码检测,迟早会导致数据损坏

# BAD, DO NOT USE
[xml]$configFile = Get-Content $PSScriptRoot\$confFile
正确读取XML文件的工作原理如下-创建一个新的XML对象并让它处理文件加载:

$configFile = New-Object xml
$configFile.Load("$PSScriptRoot\$confFile")
其次,我强烈建议不要从文件中创建全局变量。这是一种糟糕的风格,因为它可以通过盲目地覆盖现有变量而轻易破坏程序。使用散列存储文件中的值,或者直接使用XML文件作为配置文件

$config = @{}

foreach ($setting in $configFile.SelectNodes("/settings/*") ) {
    $config[$setting.Name] = $setting.InnerText
}
第三,XML没有固有的数据类型信息。在你添加更多信息之前,一切都是一个字符串。一种方法可以是
type
属性(
type=“string”
可以视为默认值):

使用
convertfromjson
cmdlet解析数据。使用
获取内容-编码UTF8
读取它

在处理文本文件时,使用
编码
参数很重要,在使用
设置内容
输出文件
编写文件时也很重要。这里没有做正确事情的隐藏魔法,您必须明确编码


下面是有关
Out File
Set Content
行为的一些更深入的信息

我同意Tomalak的回答,JSON可能更适合您的用例。下面是一个实际的例子,向您展示如何使用它。这是使用从哈希表创建的自定义对象生成JSON并将其保存到文件:

$Config = [pscustomobject]@{
    Process = $false
    xmldir = '\\serv1\dev'
    scanDir = '\\serv1\dev'
    processedDir = '\\serv1\dev\done'
    errorDir = '\\serv1\dev\err'
    log = '\\serv1\dev\log\dev-Log##DATE##.log'
    retryDelay = 5
    retryLimit = 3
}

$Config | ConvertTo-Json | Out-File .\config.txt -Encoding UTF8
这将创建如下所示的JSON:

{
    "Process":  false,
    "xmldir":  "\\\\serv1\\dev",
    "scanDir":  "\\\\serv1\\dev",
    "processedDir":  "\\\\serv1\\dev\\done",
    "errorDir":  "\\\\serv1\\dev\\err",
    "log":  "\\\\serv1\\dev\\log\\dev-Log##DATE##.log",
    "retryDelay":  5,
    "retryLimit":  3
}
$Settings = Get-Content .\config.txt -Encoding UTF8 | ConvertFrom-Json
可以这样理解:

{
    "Process":  false,
    "xmldir":  "\\\\serv1\\dev",
    "scanDir":  "\\\\serv1\\dev",
    "processedDir":  "\\\\serv1\\dev\\done",
    "errorDir":  "\\\\serv1\\dev\\err",
    "log":  "\\\\serv1\\dev\\log\\dev-Log##DATE##.log",
    "retryDelay":  5,
    "retryLimit":  3
}
$Settings = Get-Content .\config.txt -Encoding UTF8 | ConvertFrom-Json

由于您可以看到JSON存储变量的方式,PowerShell在重新读取变量时能够更好地正确键入它们。

我同意Tomalak的回答,JSON可能更适合您的用例。下面是一个实际的例子,向您展示如何使用它。这是使用从哈希表创建的自定义对象生成JSON并将其保存到文件:

$Config = [pscustomobject]@{
    Process = $false
    xmldir = '\\serv1\dev'
    scanDir = '\\serv1\dev'
    processedDir = '\\serv1\dev\done'
    errorDir = '\\serv1\dev\err'
    log = '\\serv1\dev\log\dev-Log##DATE##.log'
    retryDelay = 5
    retryLimit = 3
}

$Config | ConvertTo-Json | Out-File .\config.txt -Encoding UTF8
这将创建如下所示的JSON:

{
    "Process":  false,
    "xmldir":  "\\\\serv1\\dev",
    "scanDir":  "\\\\serv1\\dev",
    "processedDir":  "\\\\serv1\\dev\\done",
    "errorDir":  "\\\\serv1\\dev\\err",
    "log":  "\\\\serv1\\dev\\log\\dev-Log##DATE##.log",
    "retryDelay":  5,
    "retryLimit":  3
}
$Settings = Get-Content .\config.txt -Encoding UTF8 | ConvertFrom-Json
可以这样理解:

{
    "Process":  false,
    "xmldir":  "\\\\serv1\\dev",
    "scanDir":  "\\\\serv1\\dev",
    "processedDir":  "\\\\serv1\\dev\\done",
    "errorDir":  "\\\\serv1\\dev\\err",
    "log":  "\\\\serv1\\dev\\log\\dev-Log##DATE##.log",
    "retryDelay":  5,
    "retryLimit":  3
}
$Settings = Get-Content .\config.txt -Encoding UTF8 | ConvertFrom-Json

由于您可以看到JSON存储变量的方式,PowerShell在重新读取变量时能够更好地正确键入它们。

XML没有类型信息,除非您添加它。在不知道其他情况的情况下,每个元素和属性值都是一个字符串。也许您想使用JSON作为配置文件格式?除非您添加XML,否则它没有类型信息。在不知道其他情况的情况下,每个元素和属性值都是一个字符串。也许您想使用JSON作为配置文件格式?
Get Content
既不默认为UTF-8,也没有任何文件编码检测魔法,但这是许多人的基本假设。在读取/写入文本文件时,请始终使用显式的
编码设置。有关
输出文件
设置内容
的行为和处理文件编码的详细信息,请参阅此处。@Mark Wragg非常感谢。非常感谢您的帮助。
获取内容
既不是默认的UTF-8,也没有任何文件编码检测魔法,但这是许多人的基本假设。在读取/写入文本文件时,请始终使用显式的
编码设置。有关
输出文件
设置内容
的行为和处理文件编码的详细信息,请参阅此处。@Mark Wragg非常感谢。非常感谢您的帮助。感谢@Tomalak提供的信息,非常感谢!我认为按照您的建议使用JSON可能更容易。我假设我可以使用foreach遍历JSON,这与解析XML的方式类似。如果像图中所示那样构造JSON,则迭代必须基于
$config.settings | Get Member-Type NoteProperty |..
。关于这方面的更多解释,请参见。但是,正如我所说的,我不喜欢使用循环来填充配置文件中的变量。您的配置文件包含您的设置。你的程序知道设置什么