引用嵌入式哈希表元素的哈希表键语法
Hashtable key syntax to refer to embedded hashtable element
假设我有一个哈希表:
$tokens = @{
Id=9999;
Title="Lorem ipsum dolor sit amet";
Author=@{Name="John Doe"; Email='john.doe@foo.xyz'};
Analyst=@{Name="Jane Doe"; Email='jane.doe@foo.xyz'}
}
还有一个我想要填充的模板,将标记(例如 __Title__
)替换为相应哈希表的值:
/*
Author: __Author.Name__ <__Author.Email__>
Analyst: __Analyst.Name__ <__Analyst.Email__>
Request: __Title__ [__Id__]
*/
...
应该变成:
/*
Author: John Doe <john.doe@foo.xyz>
Analyst: Jane Doe <jane.doe@foo.xyz>
Request: Lorem ipsum dolor sit amet [9999]
*/
有没有办法在 'parent' 哈希表中引用嵌入式哈希表的元素? $tokens['Author.Email']
,例如,不起作用。
代码:
...
return [regex]::Replace( $template, '__(?<tokenName>\w+)__', {
# __TOKEN__
param($match)
$tokenName = $match.Groups['tokenName'].Value
if ($tokens[$tokenName]) {
# matching token returns value from hashtable;
works for simple keys `$tokens['Title']`, not complex keys `$tokens['Author.Name']`
return $tokens[$tokenName]
}
else {
# non-matching token returns token
return $match
}
})
您可以只用点符号引用元素
$tokens.author.email
如果你想检查名称是否为空,你也可以这样做。 注意有一个警告:作者应该存在才能完全按预期工作。)
If(!$tokens.author.name){$tokens.author.name = "Awesome Sauce"; }
Write-Host ("Author Name: {0}" -f $tokens.author.name)
您也可以按照 briantist
的建议使用哈希表表示法
$tokens['Author']['Email']
动态替换
你使用了动态这个词,但我不确定你想把它讲到什么程度。现在让我们假设 $tokens
元素都存在,我们将替换 here-string 中的文本。
$text = @"
/*
Author: __Author.Name__ <__Author.Email__>
Analyst: __Analyst.Name__ <__Analyst.Email__>
Request: __Title__ [__Id__]
*/
"@
$text -replace "__Author\.Name__",$tokens.Author.Name -replace "__Author\.Email__",$tokens.Author.Email `
-replace "__Analyst\.Name__",$tokens.Analyst.Name -replace "__Analyst\.Email__",$tokens.Analyst.Email `
-replace "__Title__",$tokens.Title -replace "__Id__",$tokens.Id
但我觉得您的意思是 更多 动态,因为所有这些都需要了解有关 $Tokens
和源字符串的信息。让我知道我们现在的立场。我们可以更深入地了解这一点。
让我们疯狂起来
假设您知道哈希表 $tokens
和源 $text
具有共同的值,但您不知道它们的名称。这将根据哈希表上的键名动态填充文本。目前这只适用于只有一个哈希表深度的情况。
ForEach($childKey in $tokens.Keys){
If($tokens[$childKey] -is [System.Collections.Hashtable]){
ForEach($grandChildKey in $tokens[$childKey].Keys){
Write-Host "GrandChildKey = $childKey"
$text = $text -replace "__$childKey\.$($grandChildKey)__", $tokens.$childKey.$grandChildKey
}
} Else {
$text = $text -replace "__$($childKey)__", $tokens.$childKey
}
}
$text
其他
这借鉴了 mike z 关于 Invoke-Expression
的建议,因为它减少了猜测工作。
$output = $text
$placeHolders = $text | Select-String '__([\w.]+)__' -AllMatches | ForEach-Object{$_.matches} | ForEach-Object{$_.Value}
$placeHolders.count
$placeHolders | ForEach-Object {
$output = $output -replace [regex]::Escape($_), (Invoke-Expression "`$tokens.$($_ -replace "_")")
}
$output
在 $text
中搜索所有字符串,例如 something。对于每个匹配项,将该文本替换为等效的点符号。
任一示例的输出都应与您所拥有的相匹配应变为:
几件事:
您需要修复正则表达式以实际匹配嵌套属性。现在你不需要它是 __(?<tokenName>[\w\.]+)__
使用Invoke-Expression
动态展开嵌套属性。只需构建一个代表您要评估的表达式的字符串。这很好,因为它不依赖于模型对象 $tokens
及其属性,根本不是哈希表。它所需要的只是让属性解析那里的对象。
下面是一个简短的例子。注意:如果模板来自不安全的来源,请小心并首先清理输入:
$tokens = @{
Id=9999;
Title="Lorem ipsum dolor sit amet";
Author=@{Name="John Doe"; Email='john.doe@foo.xyz'};
Analyst=@{Name="Jane Doe"; Email='jane.doe@foo.xyz'};
'3PTY' = "A";
Test=@{'Name with space' = 'x' }
}
$template = @"
/*
Author: __Author.Name__ <__Author.Email__>
Analyst: __Analyst.Name__ <__Analyst.Email__>
Request: __Title__ [__Id__]
3PTY: __"3PTY"__
Name:__Test.'Name with space'__
*/
"@
function Replace-Template {
param ([string]$template, $model)
[regex]::Replace( $template, '__(?<tokenName>[\w .\''\"]+)__', {
# __TOKEN__
# Note that TOKEN should be a valid PS property name. It may need to be enclosed in quotes
# if it starts with a number or has spaces in the name. See the example above for usage.
param($match)
$tokenName = $match.Groups['tokenName'].Value
Write-Verbose "Replacing '$tokenName'"
$tokenValue = Invoke-Expression "`$model.$tokenName" -ErrorAction SilentlyContinue
if ($tokenValue) {
# there was a value. return it.
return $tokenValue
}
else {
# non-matching token returns token
return $match
}
})
}
Replace-Template $template $tokens
输出:
/*
Author: John Doe
Analyst: Jane Doe
Request: Lorem ipsum dolor sit amet [9999]
3PTY: A
Name:x
*/
假设我有一个哈希表:
$tokens = @{
Id=9999;
Title="Lorem ipsum dolor sit amet";
Author=@{Name="John Doe"; Email='john.doe@foo.xyz'};
Analyst=@{Name="Jane Doe"; Email='jane.doe@foo.xyz'}
}
还有一个我想要填充的模板,将标记(例如 __Title__
)替换为相应哈希表的值:
/*
Author: __Author.Name__ <__Author.Email__>
Analyst: __Analyst.Name__ <__Analyst.Email__>
Request: __Title__ [__Id__]
*/
...
应该变成:
/*
Author: John Doe <john.doe@foo.xyz>
Analyst: Jane Doe <jane.doe@foo.xyz>
Request: Lorem ipsum dolor sit amet [9999]
*/
有没有办法在 'parent' 哈希表中引用嵌入式哈希表的元素? $tokens['Author.Email']
,例如,不起作用。
代码:
...
return [regex]::Replace( $template, '__(?<tokenName>\w+)__', {
# __TOKEN__
param($match)
$tokenName = $match.Groups['tokenName'].Value
if ($tokens[$tokenName]) {
# matching token returns value from hashtable;
works for simple keys `$tokens['Title']`, not complex keys `$tokens['Author.Name']`
return $tokens[$tokenName]
}
else {
# non-matching token returns token
return $match
}
})
您可以只用点符号引用元素
$tokens.author.email
如果你想检查名称是否为空,你也可以这样做。 注意有一个警告:作者应该存在才能完全按预期工作。)
If(!$tokens.author.name){$tokens.author.name = "Awesome Sauce"; }
Write-Host ("Author Name: {0}" -f $tokens.author.name)
您也可以按照 briantist
的建议使用哈希表表示法$tokens['Author']['Email']
动态替换
你使用了动态这个词,但我不确定你想把它讲到什么程度。现在让我们假设 $tokens
元素都存在,我们将替换 here-string 中的文本。
$text = @"
/*
Author: __Author.Name__ <__Author.Email__>
Analyst: __Analyst.Name__ <__Analyst.Email__>
Request: __Title__ [__Id__]
*/
"@
$text -replace "__Author\.Name__",$tokens.Author.Name -replace "__Author\.Email__",$tokens.Author.Email `
-replace "__Analyst\.Name__",$tokens.Analyst.Name -replace "__Analyst\.Email__",$tokens.Analyst.Email `
-replace "__Title__",$tokens.Title -replace "__Id__",$tokens.Id
但我觉得您的意思是 更多 动态,因为所有这些都需要了解有关 $Tokens
和源字符串的信息。让我知道我们现在的立场。我们可以更深入地了解这一点。
让我们疯狂起来
假设您知道哈希表 $tokens
和源 $text
具有共同的值,但您不知道它们的名称。这将根据哈希表上的键名动态填充文本。目前这只适用于只有一个哈希表深度的情况。
ForEach($childKey in $tokens.Keys){
If($tokens[$childKey] -is [System.Collections.Hashtable]){
ForEach($grandChildKey in $tokens[$childKey].Keys){
Write-Host "GrandChildKey = $childKey"
$text = $text -replace "__$childKey\.$($grandChildKey)__", $tokens.$childKey.$grandChildKey
}
} Else {
$text = $text -replace "__$($childKey)__", $tokens.$childKey
}
}
$text
其他
这借鉴了 mike z 关于 Invoke-Expression
的建议,因为它减少了猜测工作。
$output = $text
$placeHolders = $text | Select-String '__([\w.]+)__' -AllMatches | ForEach-Object{$_.matches} | ForEach-Object{$_.Value}
$placeHolders.count
$placeHolders | ForEach-Object {
$output = $output -replace [regex]::Escape($_), (Invoke-Expression "`$tokens.$($_ -replace "_")")
}
$output
在 $text
中搜索所有字符串,例如 something。对于每个匹配项,将该文本替换为等效的点符号。
任一示例的输出都应与您所拥有的相匹配应变为:
几件事:
您需要修复正则表达式以实际匹配嵌套属性。现在你不需要它是
__(?<tokenName>[\w\.]+)__
使用
Invoke-Expression
动态展开嵌套属性。只需构建一个代表您要评估的表达式的字符串。这很好,因为它不依赖于模型对象$tokens
及其属性,根本不是哈希表。它所需要的只是让属性解析那里的对象。
下面是一个简短的例子。注意:如果模板来自不安全的来源,请小心并首先清理输入:
$tokens = @{
Id=9999;
Title="Lorem ipsum dolor sit amet";
Author=@{Name="John Doe"; Email='john.doe@foo.xyz'};
Analyst=@{Name="Jane Doe"; Email='jane.doe@foo.xyz'};
'3PTY' = "A";
Test=@{'Name with space' = 'x' }
}
$template = @"
/*
Author: __Author.Name__ <__Author.Email__>
Analyst: __Analyst.Name__ <__Analyst.Email__>
Request: __Title__ [__Id__]
3PTY: __"3PTY"__
Name:__Test.'Name with space'__
*/
"@
function Replace-Template {
param ([string]$template, $model)
[regex]::Replace( $template, '__(?<tokenName>[\w .\''\"]+)__', {
# __TOKEN__
# Note that TOKEN should be a valid PS property name. It may need to be enclosed in quotes
# if it starts with a number or has spaces in the name. See the example above for usage.
param($match)
$tokenName = $match.Groups['tokenName'].Value
Write-Verbose "Replacing '$tokenName'"
$tokenValue = Invoke-Expression "`$model.$tokenName" -ErrorAction SilentlyContinue
if ($tokenValue) {
# there was a value. return it.
return $tokenValue
}
else {
# non-matching token returns token
return $match
}
})
}
Replace-Template $template $tokens
输出:
/*
Author: John Doe
Analyst: Jane Doe
Request: Lorem ipsum dolor sit amet [9999]
3PTY: A
Name:x
*/