假设我有一个哈希表:
$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]
*/
有没有办法在父母中引用嵌入式哈希表的元素&#39;哈希表?例如,$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
}
})
答案 0 :(得分:2)
您可以使用点表示法引用元素
$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
中搜索所有字符串,例如某事。对于每个匹配,用等号的点符号替换该文本。
任何一个样本的输出应该与应该成为:
的输出相匹配答案 1 :(得分:2)
一些事情:
您需要修复正则表达式以实际匹配嵌套属性。现在你不需要它__(?<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
输出:
/ *
作者:John Doe
分析师:Jane Doe 要求:Lorem ipsum dolor sit amet [9999]
3PTY:A
名称:X
* /