7

我的应用程序中有以下 XML 解析代码:

    public static XElement Parse(string xml, string xsdFilename)
    {
        var readerSettings = new XmlReaderSettings
        {
            ValidationType = ValidationType.Schema,
            Schemas = new XmlSchemaSet()
        };
        readerSettings.Schemas.Add(null, xsdFilename);
        readerSettings.ValidationFlags |= XmlSchemaValidationFlags.ProcessInlineSchema;
        readerSettings.ValidationFlags |= XmlSchemaValidationFlags.ProcessSchemaLocation;
        readerSettings.ValidationFlags |= XmlSchemaValidationFlags.ReportValidationWarnings;
        readerSettings.ValidationEventHandler +=
            (o, e) => { throw new Exception("The provided XML does not validate against the request's schema."); };

        var readerContext = new XmlParserContext(null, null, null, XmlSpace.Default, Encoding.UTF8);

        return XElement.Load(XmlReader.Create(new StringReader(xml), readerSettings, readerContext));
    }

我正在使用它将发送到我的 WCF 服务的字符串解析为 XML 文档,以进行自定义反序列化。

当我读入文件并通过网络发送它们(请求)时,它工作正常;我已验证 BOM 未发送。在我的请求处理程序中,我正在序列化一个响应对象并将其作为字符串发送回来。序列化过程会在字符串的前面添加一个 UTF-8 BOM,这会导致解析响应时相同的代码中断。

System.Xml.XmlException : Data at the root level is invalid. Line 1, position 1.

在我过去一个小时左右所做的研究中,XmlReader 似乎应该遵守 BOM。如果我从字符串的前面手动删除 BOM,则响应 xml 解析得很好。

我是否遗漏了一些明显的东西,或者至少是一些阴险的东西?

编辑:这是我用来返回响应的序列化代码:

private static string SerializeResponse(Response response)
{
    var output = new MemoryStream();
    var writer = XmlWriter.Create(output);
    new XmlSerializer(typeof(Response)).Serialize(writer, response);
    var bytes = output.ToArray();
    var responseXml = Encoding.UTF8.GetString(bytes);
    return responseXml;
}

如果这只是 xml 错误地包含 BOM 的问题,那么我将切换到

var responseXml = new UTF8Encoding(false).GetString(bytes);

但从我的研究中根本不清楚 BOM 在实际的 XML 字符串中是非法的;参见例如c# Detect xml encoding from Byte Array?

4

5 回答 5

9

在我的请求处理程序中,我正在序列化一个响应对象并将其作为字符串发送回来。序列化过程会在字符串的前面添加一个 UTF-8 BOM,这会导致解析响应时相同的代码中断。

因此,您希望防止在序列化过程中添加 BOM。不幸的是,您没有提供您的序列化逻辑是什么。

您应该做的是提供一个通过UTF8Encoding(bool)构造函数创建的UTF8Encoding实例来禁用 BOM 的生成,并将此实例传递给您正在使用的生成中间字符串的任何方法。Encoding

于 2010-06-23T17:57:07.503 回答
6

xml 字符串不能 (!) 包含 BOM,BOM 仅允许在使用 UTF-8 编码的字节数据(例如流)中。这是因为字符串表示没有被编码,而是已经是一个 unicode 字符序列。

因此,您似乎加载了错误的字符串,这是您不幸没有提供的代码。

编辑:

感谢您发布序列化代码。

您不应该将数据写入 MemoryStream,而是写入 StringWriter,然后您可以使用 ToString 将其转换为字符串。由于这避免了通过字节表示,它不仅更快,而且还避免了此类问题。

像这样的东西:

private static string SerializeResponse(Response response)
{
    var output = new StringWriter();
    var writer = XmlWriter.Create(output);
    new XmlSerializer(typeof(Response)).Serialize(writer, response);
    return output.ToString();
}
于 2010-06-23T17:49:40.810 回答
0

BOM 首先不应该在字符串中。
BOM 用于检测原始字节数组的编码;他们与实际字符串无关。

字符串从何而来?
您可能正在使用错误的编码阅读它。

于 2010-06-23T17:49:54.737 回答
0

C# 中的字符串被编码为 UTF-16,因此 BOM 将是错误的。作为一般规则,始终将 XML 编码为字节数组并从字节数组中解码。

于 2010-06-23T17:50:47.620 回答
0

下面是如何将 MemoryStream 转换为可以与 XmlDocument 一起使用的字符串(Skip 函数是 Linq):

    public static string Decode(MemoryStream ms)
    {
      var fileBytes = ms.ToArray();
      var isUnicode = fileBytes[0] == 0xff && fileBytes[1] == 0xfe;  //UTF-16 little endian
      var isUtf8Bom = fileBytes[0] == 0xef && fileBytes[1] == 0xbb && fileBytes[2] == 0xbf;

      string xml = isUnicode ? Encoding.Unicode.GetString(fileBytes) : 
                   (isUtf8Bom ? new UTF8Encoding(false, true).GetString(fileBytes.Skip(3).ToArray()) : new UTF8Encoding(false, true).GetString(fileBytes));

       return xml;
    } 
于 2022-02-10T07:25:35.553 回答