关于.net:十六进制值0x00是无效字符

Hexadecimal value 0x00 is a invalid character

我从StringBuilder生成XML文档,基本上是这样的:

1
string.Format("<text><row>{0}</row><col>{1}</col><textHeight>{2}</textHeight><textWidth>{3}</textWidth><data>{4}</data><rotation>{5}</rotation></text>

后来,类似:

1
2
3
4
5
6
7
XmlDocument document = new XmlDocument();
document.LoadXml(xml);
XmlNodeList labelSetNodes = document.GetElementsByTagName("labels");
for (int index = 0; index < labelSetNodes.Count; index++)
{
    //do something
}

所有数据都来自数据库。
最近,我遇到了一些有关该错误的问题:

Hexadecimal value 0x00 is a invalid character, line 1, position nnnnn

但是它并不一致。
有时某些"空白"数据会起作用。
"故障"数据在某些PC上有效,但在其他PC上则不可用。

在数据库中,数据始终为空字符串。 永远不会为空
并在XML文件中以< data>< /data>的形式显示,即打开和关闭之间没有字符。 (但不确定是否可以依靠它,因为我是从Studio的"即时"窗口中拉出它并将其粘贴到文本板中的)。

sql server的版本(2008失败,2005可以工作)和排序规则也可能存在差异。
不确定是否有任何可能的原因?

但是完全相同的代码和数据有时会失败。 任何想法出在哪里?


没有您的实际数据或来源,我们将很难诊断出问题所在。但是,我可以提出一些建议:

  • Unicode NUL(0x00)在所有版本的XML中都是非法的,并且验证解析器必须拒绝包含它的输入。
  • 尽管有上述情况;真实世界中未经验证的XML可以包含可以想象的任何形式的乱码。
  • XML 1.1允许使用零宽度和非打印控制字符(NUL除外),因此您无法在文本编辑器中查看XML 1.1文件并告诉其包含哪些字符。

鉴于您所写的内容,我怀疑将数据库数据转换为XML的方法是否已损坏;它正在传播非XML字符。

使用非XML字符(NUL,DEL,控制字符等)创建一些数据库条目,然后在其上运行XML转换器。将XML输出到文件,然后在十六进制编辑器中查看它。如果其中包含非XML字符,则您的转换器已损坏。对其进行修复,或者,如果不能,则创建一个预处理器以拒绝带有此类字符的输出。

如果转换器输出看起来不错,则问题出在您的XML使用方中;它在某处插入非XML字符。您将必须将消耗过程分为几个步骤,检查每个步骤的输出,并缩小引入不良字符的范围。

检查文件编码(用于UTF-16)

更新:我自己碰到了一个例子!发生的事情是,生产者将XML编码为UTF16,而消费者则期望UTF8。由于UTF16将0x00用作所有ASCII字符的高字节,而UTF8则不使用,因此使用者将第二个字节视为NUL。就我而言,我可以更改编码,但是建议所有XML有效负载均以BOM表开头。


就我而言,这需要花一些时间才能找到。

我的背景

我正在使用Elmah从网站查看异常/错误日志。 Elmah在异常发生时以大XML文档的形式返回服务器的状态。对于我们的报告引擎,我使用XmlWriter漂亮地打印了XML。

在网站攻击期间,我注意到某些xml文件没有解析,并且正在接收此'.', hexadecimal value 0x00, is an invalid character.异常。

NON-RESOLUTION:我将文档转换为byte[]并将其清除为0x00,但没有找到。

当我扫描xml文档时,发现以下内容:

1
2
3
4
5
6
7
8
...
<form>
...
<item name="SomeField">
   <value
     string="C:\\boot.ini .htm" />
 </item>
...

有一个nul字节编码为html实体

解决方案:为了解决编码问题,我在将 值加载到我的XmlDocument中之前替换了它,因为加载它会创建nul字节,并且很难从对象中清除它。这是我的整个过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
XmlDocument xml = new XmlDocument();
details.Xml = details.Xml.Replace("","[0x00]");  // in my case I want to see it, otherwise just replace with""
xml.LoadXml(details.Xml);

string formattedXml = null;

// I have this in a helper function, but for this example I have put it in-line
StringBuilder sb = new StringBuilder();
XmlWriterSettings settings = new XmlWriterSettings {
    OmitXmlDeclaration = true,
    Indent = true,
    IndentChars ="\\t",
    NewLineHandling = NewLineHandling.None,
};
using (XmlWriter writer = XmlWriter.Create(sb, settings)) {
    xml.Save(writer);
    formattedXml = sb.ToString();
}

经验教训:如果传入的数据在输入时是html编码的,则使用关联的html实体清除非法字节。


为了补充上面的Sonz回答,以下对我们有用。

1
2
3
4
//Instead of
XmlString.Replace("","[0x00]");
// use this
XmlString.Replace("\\x00","[0x00]");


当我在Web.config文件中保存一些unicode数据(印地语)并使用" Unicode"编码保存时,在ASP.NET应用程序中也会遇到相同的错误。

当我使用" UTF-8"编码保存Web.config文件时,它为我解决了该错误。


作为一个较晚的答案:

上传报告时,SSRS ReportService2005.asmx存在此问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
    Public Shared Sub CreateReport(ByVal strFileNameAndPath As String, ByVal strReportName As String, ByVal strReportingPath As String, Optional ByVal bOverwrite As Boolean = True)
        Dim rs As SSRS_2005_Administration_WithFOA = New SSRS_2005_Administration_WithFOA
        rs.Credentials = ReportingServiceInterface.GetMyCredentials(strCredentialsURL)
        rs.Timeout = ReportingServiceInterface.iTimeout
        rs.Url = ReportingServiceInterface.strReportingServiceURL
        rs.UnsafeAuthenticatedConnectionSharing = True

        Dim btBuffer As Byte() = Nothing

        Dim rsWarnings As Warning() = Nothing
        Try
            Dim fstrStream As System.IO.FileStream = System.IO.File.OpenRead(strFileNameAndPath)
            btBuffer = New Byte(fstrStream.Length - 1) {}
            fstrStream.Read(btBuffer, 0, CInt(fstrStream.Length))
            fstrStream.Close()
        Catch ex As System.IO.IOException
            Throw New Exception(ex.Message)
        End Try

        Try
            rsWarnings = rs.CreateReport(strReportName, strReportingPath, bOverwrite, btBuffer, Nothing)

            If Not (rsWarnings Is Nothing) Then
                Dim warning As Warning
                For Each warning In rsWarnings
                    Log(warning.Message)
                Next warning
            Else
                Log("Report: {0} created successfully with no warnings", strReportName)
            End If

        Catch ex As System.Web.Services.Protocols.SoapException
            Log(ex.Detail.InnerXml.ToString())
        Catch ex As Exception
            Log("Error at creating report. Invalid server name/timeout?" + vbCrLf + vbCrLf +"Error Description:" + vbCrLf + ex.Message)
            Console.ReadKey()
            System.Environment.Exit(1)
        End Try
    End Sub ' End Function CreateThisReport

当您分配一个比RDL(XML)文件大至少1个字节的字节数组时,会发生此问题。

具体来说,我使用了C#到vb.net转换器,

1
  btBuffer = new byte[fstrStream.Length];

进入

1
  btBuffer = New Byte(fstrStream.Length) {}

但是因为在C#中,数字表示数组中的元素数,而在VB.NET中,该数字表示数组的上限,所以我有一个多余的字节,从而导致此错误。

因此,问题的解决方案很简单:

1
  btBuffer = New Byte(fstrStream.Length - 1) {}

我在这里使用IronPython(与.NET API相同),并以UTF-8格式读取文件,以便正确处理BOM表为我解决了该问题:

1
2
xmlFile = Path.Combine(directory_str, 'file.xml')
doc = XPathDocument(XmlTextReader(StreamReader(xmlFile.ToString(), Encoding.UTF8)))

XmlDocument也可以使用:

1
2
doc = XmlDocument()
doc.Load(XmlTextReader(StreamReader(xmlFile.ToString(), Encoding.UTF8)))