在 Windows PowerShell v5.1 中,ConvertTo-Json 确实意外地将 & 字符编码为 Unicode 转义序列 \u0026,其中 0026 表示十六进制。数字 0x26,表示 & character, U+0026 的 Unicode 代码点。
(相比之下,PowerShell Core 保留了 & 原样。)
也就是说,JSON 解析器应该能够解释这样的转义序列,事实上,互补的 ConvertFrom-Json cmdlet 是。
- 注意:下面的解决方案是通用的,可以处理any Unicode字符的Unicode转义序列;由于
ConvertTo-Json 似乎只对字符 &、'、< 和 > 使用这些 Unicode 转义序列表示,因此更简单的解决方案是可能,除非必须排除误报 - 请参阅this answer。
也就是说,如果您确实想手动将 Unicode 转义序列转换为 JSON 文本中的等效字符,您可以使用以下 - 有限的解决方案:
# Sample JSON with Unicode escapes.
$json = '{ "roleFullPath": "Applications\\User Admin \u0026 Support-DEMO" }'
# Replace Unicode escapes with the chars. they represent,
# with limitations.
[regex]::replace($json, '\\u[0-9a-fA-F]{4}', {
param($match) [char] [int] ('0x' + $match.Value.Substring(2))
})
以上产出:
{ "roleFullPath": "Applications\\User Admin & Support-DEMO" }
注意\u0026 是如何转换为字符的。它代表&。
稳健的解决方案需要更多工作:
-
有些字符必须在 JSON 中转义并且不能按字面意思表示,所以为了 to-character 转换能够正常工作 ,这些字符必须排除。
-
此外,必须避免误报;例如,\\u0026不是有效的 Unicode 转义序列,因为 JSON 解析器将 \\ 解释为转义的 \,后跟逐字的 u0026。
-
最后," 和 \ 的 Unicode 序列必须被翻译成它们的 转义 形式,\" 和 \\,并且可以表示几个 ASCII 范围通过 C 样式的转义序列控制字符,例如,\t 用于制表符 (\u0009)。
以下稳健的解决方案解决了所有这些问题:
# Sample JSON with Unicode escape sequences:
# \u0026 is &, which CAN be converted to the literal char.
# \u000a is a newline (LF) character, which CANNOT be converted, but can
# be translated to escape sequence "\n"
# \\u0026 is *not* a Unicode escape sequence and must be preserved as-is.
$json = '{
"roleFullPath": "Applications\u000aUser Admin \u0026 Support-DEMO-\\u0026"
}'
[regex]::replace($json, '(?<=(?:^|[^\\])(?:\\\\)*)\\u([0-9a-fA-F]{4})', {
param($match)
$codePoint = [int] ('0x' + $match.Groups[1].Value)
if ($codePoint -in 0x22, 0x5c) {
# " or \ must be \-escaped.
'\' + [char] $codePoint
}
elseif ($codePoint -in 0x8, 0x9, 0xa, 0xc, 0xd) {
# Control chars. that can be represented as short, C-style escape sequences.
('\b', '\t', '\n', $null, '\f', '\r')[$codePoint - 0x8]
}
elseif ($codePoint -le 0x1f -or [char]::IsSurrogate([char] $codePoint)) {
# Other control chars. and halves of surrogate pairs must be retained
# as escape sequences.
# (Converting surrogate pairs to a single char. would require much more effort.)
$match.Value
}
else {
# Translate to literal char.
[char] $codePoint
}
})
输出:
{
"roleFullPath": "Applications\nUser Admin & Support-DEMO-\\u0026"
}