Powershell xml 解析打乱了编码
Powershell xml parsing messes up the encoding
我有这个非常简单的脚本:
$rssUrl = "https://elpais.com/rss/elpais/portada.xml"
$FeedXml = [xml](Invoke-WebRequest $rssUrl)
此时如果我调用 $FeedXml.Save()
提要中的所有重音符号和特殊字符都乱七八糟,就好像编码错误一样。
例如:
Un periodista que viaj?? a Mil??n para
should be:
Un periodista que viajó a Milán para
但是 (Invoke-WebRequest $rssUrl).Content
产生了正确的输出。
我目前已经这样做了:
$FeedXml = New-Object xml
$resolver = New-Object -TypeName System.Xml.XmlUrlResolver
$resolver.Credentials = [System.Net.CredentialCache]::DefaultCredentials
$reader = New-Object -TypeName System.Xml.XmlReaderSettings
$reader.XmlResolver = $resolver
$reader = [System.Xml.XmlReader]::Create($rssUrl, $reader)
$FeedXml.Load($reader)
在那种情况下 $FeedXml.Save()
产生预期的输出。
我完全无法理解为什么第一个代码(应该是“正确的方式”)不起作用?
所以看起来问题是,当 PowerShell 将 Invoke-WebRequest $rssUrl
的结果转换为 xml 文档时,它使用 [System.Text.Encoding]::ASCII
将原始字节流转换为字符串,并且在你的情况下,根据 http 请求中的 headers 实际上是一个 utf8 字节流。
PS> $rssUrl = "https://elpais.com/rss/elpais/portada.xml"
PS> $response = Invoke-WebRequest $rssUrl
PS> $response.GetType().FullName
Microsoft.PowerShell.Commands.BasicHtmlWebResponseObject
PS> $response.Headers["Content-Type"]
text/xml; charset=utf-8
这是 BasicHtmlWebResponseObject 的来源:https://github.com/PowerShell/PowerShell/blob/master/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/BasicHtmlWebResponseObject.Common.cs
您可以看到它继承自 WebResponseObject,其 ToString 方法位于此处:https://github.com/PowerShell/PowerShell/blob/658837323599ab1c7a81fe66fcd43f7420e4402b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/WebResponseObject.Common.cs#L88
/// <summary>
/// Returns the string representation of this web response.
/// </summary>
/// <returns>The string representation of this web response.</returns>
public sealed override string ToString()
{
char[] stringContent = System.Text.Encoding.ASCII.GetChars(Content);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^
for (int counter = 0; counter < stringContent.Length; counter++)
{
if (!IsPrintable(stringContent[counter]))
{
stringContent[counter] = '.';
}
}
return new string(stringContent);
}
另一方面,(Invoke-WebRequest $rssUrl).Content
是一个已使用 System.Text.Encoding.UTF8
正确解码的字符串,因此它保留了重音字符。
简而言之,最好使用已经是字符串的 (Invoke-WebRequest $rssUrl).Content
,而不是仅使用 Invoke-WebRequest $rssUrl
.
隐式转换为字符串。
我有这个非常简单的脚本:
$rssUrl = "https://elpais.com/rss/elpais/portada.xml"
$FeedXml = [xml](Invoke-WebRequest $rssUrl)
此时如果我调用 $FeedXml.Save()
提要中的所有重音符号和特殊字符都乱七八糟,就好像编码错误一样。
例如:
Un periodista que viaj?? a Mil??n para
should be:
Un periodista que viajó a Milán para
但是 (Invoke-WebRequest $rssUrl).Content
产生了正确的输出。
我目前已经这样做了:
$FeedXml = New-Object xml
$resolver = New-Object -TypeName System.Xml.XmlUrlResolver
$resolver.Credentials = [System.Net.CredentialCache]::DefaultCredentials
$reader = New-Object -TypeName System.Xml.XmlReaderSettings
$reader.XmlResolver = $resolver
$reader = [System.Xml.XmlReader]::Create($rssUrl, $reader)
$FeedXml.Load($reader)
在那种情况下 $FeedXml.Save()
产生预期的输出。
我完全无法理解为什么第一个代码(应该是“正确的方式”)不起作用?
所以看起来问题是,当 PowerShell 将 Invoke-WebRequest $rssUrl
的结果转换为 xml 文档时,它使用 [System.Text.Encoding]::ASCII
将原始字节流转换为字符串,并且在你的情况下,根据 http 请求中的 headers 实际上是一个 utf8 字节流。
PS> $rssUrl = "https://elpais.com/rss/elpais/portada.xml"
PS> $response = Invoke-WebRequest $rssUrl
PS> $response.GetType().FullName
Microsoft.PowerShell.Commands.BasicHtmlWebResponseObject
PS> $response.Headers["Content-Type"]
text/xml; charset=utf-8
这是 BasicHtmlWebResponseObject 的来源:https://github.com/PowerShell/PowerShell/blob/master/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/BasicHtmlWebResponseObject.Common.cs
您可以看到它继承自 WebResponseObject,其 ToString 方法位于此处:https://github.com/PowerShell/PowerShell/blob/658837323599ab1c7a81fe66fcd43f7420e4402b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/WebResponseObject.Common.cs#L88
/// <summary>
/// Returns the string representation of this web response.
/// </summary>
/// <returns>The string representation of this web response.</returns>
public sealed override string ToString()
{
char[] stringContent = System.Text.Encoding.ASCII.GetChars(Content);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^
for (int counter = 0; counter < stringContent.Length; counter++)
{
if (!IsPrintable(stringContent[counter]))
{
stringContent[counter] = '.';
}
}
return new string(stringContent);
}
另一方面,(Invoke-WebRequest $rssUrl).Content
是一个已使用 System.Text.Encoding.UTF8
正确解码的字符串,因此它保留了重音字符。
简而言之,最好使用已经是字符串的 (Invoke-WebRequest $rssUrl).Content
,而不是仅使用 Invoke-WebRequest $rssUrl
.