根级别的Azure Web App XML数据无效。第1行,位置1

时间:2019-07-15 18:41:51

标签: c# asp.net encoding azure-web-app-service

给出两个XML文件,其内容开始如下:

文件A

<?xml

文件B

<?xml

文件B将错误。因此,我们实施了以下代码来删除BOM,后者已在生产环境中使用了多年:

private static string RemoveUTF8ByteOrderMark(string str)
{
    var byteOrderMarkUtf8 = Encoding.UTF8.GetString(Encoding.UTF8.GetPreamble());

    if (str.StartsWith(byteOrderMarkUtf8))
    {    
        str = str.Remove(0, byteOrderMarkUtf8.Length);
    }

    return str;
}

一旦删除BOM,我们将使用以下命令将字符串解析为XML:

public static XDocument ParseXmlDocumentFromText(string fileText)
{
    if (string.IsNullOrEmpty(fileText)) return null;

    var nsm = new XmlNamespaceManager(new NameTable());

    nsm.AddNamespace("*****", "*****");

    var ctx = new XmlParserContext(null, nsm, null, XmlSpace.Default);
    var settings = new XmlReaderSettings { ProhibitDtd = false, XmlResolver = null };

    using (var fs = new StringReader(fileText))
    {
        using (var reader = XmlReader.Create(fs, settings, ctx))
        {
            var doc = new XmlDocument();

            doc.Load(reader);

            return XDocument.Parse(doc.OuterXml);
        }
    }
}

现在,我们已经将网站从专用服务器迁移到Azure Web App,并且在文件B继续正确加载的同时,文件A在doc.Load(reader);处出错。

  

System.Web.HttpUnhandledException(0x80004005):类型的异常   引发了“ System.Web.HttpUnhandledException”。 ->   System.Xml.XmlException:根级别的数据无效。 1号线   位置1。

在Azure中,如果我禁用了RemoveUTF8ByteOrderMark(...)调用,则文件A正确加载,并且文件B错误(按预期)。

当我在计算机上进行本地测试时,两个文件都启用了RemoveUTF8ByteOrderMark(...)加载,这与我们的旧专用服务器一致。在所有3种环境中,XML文件都是从Azure blob存储中提取的,因此始终使用完全相同的文件。

Azure Web App中正在发生什么,正在更改此代码的运行方式?

更新

在Azure中,调用RemoveUTF8ByteOrderMark(...)时,我看到返回的文本如下:

文件A

?xml 

文件B

<?xml 

那为什么RemoveUTF8ByteOrderMark(...)显然导致开头的<被剥夺了?

1 个答案:

答案 0 :(得分:1)

看来,您的代码对于使用UTF-8编码处理文件没有问题。但是,根据Wiki页面Byte order mark,文件中BOM表头的字节长度不同,编码方式也不同,如下图所示。

enter image description here

因此,一种用于删除通用文件BOM头的解决方案,您需要首先检测文件编码,然后删除文件头的不同长度的字节以获取没有BOM的真实内容。

C#和C ++中有一个GitHub存储库AutoItConsulting/text-encoding-detect,可帮助检测文本内容编码并检查BOM,如下the code所示。

/// <summary>
///     Gets the BOM length for a given Encoding mode.
/// </summary>
/// <param name="encoding"></param>
/// <returns>The BOM length.</returns>
public static int GetBomLengthFromEncodingMode(Encoding encoding)
{
    int length;

    switch (encoding)
    {
        case Encoding.Utf16BeBom:
        case Encoding.Utf16LeBom:
            length = 2;
            break;

        case Encoding.Utf8Bom:
            length = 3;
            break;

        default:
            length = 0;
            break;
    }

    return length;
}

/// <summary>
///     Checks for a BOM sequence in a byte buffer.
/// </summary>
/// <param name="buffer"></param>
/// <param name="size"></param>
/// <returns>Encoding type or Encoding.None if no BOM.</returns>
public Encoding CheckBom(byte[] buffer, int size)
{
    // Check for BOM
    if (size >= 2 && buffer[0] == _utf16LeBom[0] && buffer[1] == _utf16LeBom[1])
    {
        return Encoding.Utf16LeBom;
    }

    if (size >= 2 && buffer[0] == _utf16BeBom[0] && buffer[1] == _utf16BeBom[1])
    {
        return Encoding.Utf16BeBom;
    }

    if (size >= 3 && buffer[0] == _utf8Bom[0] && buffer[1] == _utf8Bom[1] && buffer[2] == _utf8Bom[2])
    {
        return Encoding.Utf8Bom;
    }

    return Encoding.None;
}

我认为您可以直接使用或更改这些代码来解决问题,以删除文件中可能的BOM字节。

同时,如果您只需要处理XML文件的内容,我认为一种简单的方法是将IndexOf<?xml一起使用具有可能的BOM字节的字符串,因为<?xml字符串是XML DTD中的固定内容。

这是我的示例代码,它适用于带有或不带有BOM字节的xml文件。

int index = str.IndexOf("<?xml");
return str.Substring(index);

希望有帮助。