ConvertTo-Json带有或不带Unscape的特殊字符 - 结果错误

时间:2017-12-12 18:23:53

标签: powershell

我有一个包含某些属性的文件,其中一些属性的值包含转义字符,例如一些Urls和Regex模式。

在阅读内容并转换回json时,无论有没有取消内容,内容都不正确。如果我用unescaping转换回json,一些正则表达式中断,如果我转换为unescaping,url和一些正则表达式将会中断。

我该如何解决这个问题?

MCVE

以下是一些简单的代码块,可让您简单地重现问题:

内容

$fileContent = 
@"
{
    "something":  "http://domain/?x=1&y=2",
    "pattern":  "^(?!(\\`|\\~|\\!|\\@|\\#|\\$|\\||\\\\|\\'|\\\")).*"
}
"@

使用Unescape

如果我阅读内容,然后使用以下命令将内容转换回json:

$fileContent | ConvertFrom-Json | ConvertTo-Json | %{[regex]::Unescape($_)}

输出(错误)将是:

{
    "something":  "http://domain/?x=1&y=2",
    "pattern":  "^(?!(\|\~|\!|\@|\#|\$|\||\\|\'|\")).*"
}

没有Unescape

如果我阅读内容,然后使用以下命令将内容转换回json:

$fileContent | ConvertFrom-Json | ConvertTo-Json 

输出(错误)将是:

{
    "something":  "http://domain/?x=1\u0026y=2",
    "pattern":  "^(?!(\\|\\~|\\!|\\@|\\#|\\$|\\||\\\\|\\\u0027|\\\")).*"
}

2 个答案:

答案 0 :(得分:6)

我决定使用Unscape,而是使用字符串值替换unicode \uxxxx字符,现在它可以正常运行:

$fileContent = 
@"
{
    "something":  "http://domain/?x=1&y=2",
    "pattern":  "^(?!(\\`|\\~|\\!|\\@|\\#|\\$|\\||\\\\|\\'|\\\")).*"
}
"@

$fileContent | ConvertFrom-Json | ConvertTo-Json | %{
    [Regex]::Replace($_, 
        "\\u(?<Value>[a-zA-Z0-9]{4})", {
            param($m) ([char]([int]::Parse($m.Groups['Value'].Value,
                [System.Globalization.NumberStyles]::HexNumber))).ToString() } )}

生成预期输出:

{
    "something":  "http://domain/?x=1&y=\\2",
    "pattern":  "^(?!(\\|\\~|\\!|\\@|\\#|\\$|\\||\\\\|\\'|\\\")).*"
}

答案 1 :(得分:2)

如果不想依靠Regex(来自@Reza Aghaei的答案),则可以导入Newtonsoft JSON库。好处是默认的StringEscapeHandling属性仅转义控制字符。另一个好处是避免了使用Regex进行潜在的危险字符串替换。

StringEscapeHandling也是PowerShell Core(版本6及更高版本)的默认处理方式,因为从那时起,他们开始在内部使用Newtonsoft。因此,另一种替代方法是使用PowerShell Core中的ConvertFrom-Json和ConvertTo-Json。

如果您导入Newtonsoft JSON库,您的代码将类似于以下内容:

[Reflection.Assembly]::LoadFile("Newtonsoft.Json.dll")

$json = Get-Content -Raw -Path file.json -Encoding UTF8 # read file
$unescaped = [Newtonsoft.Json.Linq.JObject]::Parse($json) # similar to ConvertFrom-Json

$escapedElementValue = [Newtonsoft.Json.JsonConvert]::ToString($unescaped.apiName.Value) # similar to ConvertTo-Json
$escapedCompleteJson = [Newtonsoft.Json.JsonConvert]::SerializeObject($unescaped) # similar to ConvertTo-Json

Write-Output "Variable passed = $escapedElementValue"
Write-Output "Same JSON as Input = $escapedCompleteJson"