xml.LoadData - 根级别的数据无效。第1行,第1个位置。

105

我正在尝试解析WiX安装程序中的一些XML。这个XML是从Web服务器返回的所有错误的对象。使用以下代码,我会得到问题标题中的错误:

XmlDocument xml = new XmlDocument();
try
{
    xml.LoadXml(myString);
}
catch (Exception ex)
{
    System.IO.File.WriteAllText(@"C:\text.txt", myString + "\r\n\r\n" + ex.Message);
    throw ex;
}

myString 是这个(在 text.txt 输出中看到)

<?xml version="1.0" encoding="utf-8"?>
<Errors></Errors>

text.txt 的输出结果如下所示:

<?xml version="1.0" encoding="utf-8"?>
<Errors></Errors>

Data at the root level is invalid. Line 1, position 1.

我需要解析这个XML文件,以便查看是否存在任何错误。


1
@marc_s:一个string能否真正成为UTF-8?如果处理指令(第一行)在加载之前被删除会怎样? - John Saunders
2
如果我拿你的代码编译并运行它,我不会得到任何错误。但这可能是因为我硬编码了myString。你的myString是如何设置的呢? 如果它来自另一个文件或流,那么文件顶部可能会有令人讨厌的字节顺序标记。通常编辑器不会显示它(除非它们有十六进制模式)。 - Richard
看起来似乎可以在没有第一行的情况下解析。让我确保错误可以以这种方式处理。很抱歉花费了这么长时间。每次我想要测试,都必须重新构建整个WiX安装程序。 - Chris
@Richard - 这个来自于从远程服务器的服务调用。 - Chris
如果在文件中有任何内容位于此行之上:<?xml version="1.0" encoding="utf-8"?>,请将其从文件中删除并重试。 - Jo Smo
12个回答

175
隐藏的字符可能是BOM。 可以在这里找到有关该问题和解决方案的说明,感谢James Schubert,基于James Brankin在这里找到的答案。
尽管之前的答案确实删除了隐藏字符,但也删除了整个第一行。更精确的版本应该是:
string _byteOrderMarkUtf8 = Encoding.UTF8.GetString(Encoding.UTF8.GetPreamble());
if (xml.StartsWith(_byteOrderMarkUtf8))
{
    xml = xml.Remove(0, _byteOrderMarkUtf8.Length);
}

当我从Azure blob获取一个XSLT文件并将其加载到XslCompiledTransform对象中时,我遇到了这个问题。在我的机器上,该文件看起来很正常,但是在将其作为blob上传并重新获取后,BOM字符被添加了。


3
不确定,我猜我得继续寻找,但当我这样做时,_byteOrderMarkUtf8 = ""。所以它无法捕捉到它。有什么想法吗? - user1040975
2
尝试过了,没有帮助。XML 是来自数据库的。 - John Demetriou
1
Encoding.UTF8.GetString(Encoding.UTF8.GetPreamble())的评估结果为空字符串。 - Mister Cook
8
和上面的评论者一样遇到了相同的问题。对我来说,使用xmlStartsWith(byteOrderMarkUtf8, StringComparison.Ordinal)起了作用。感谢Hans Passant:https://dev59.com/questions/w2Ik5IYBdhLWcg3wMLmI#19495964 - Polshgiant
2
这对我解决了问题,非常感谢你,我已经为此苦恼了一段时间。 - mknopf
显示剩余6条评论

86

请使用 Load() 方法代替,这可以解决问题。 查看更多


22
我正在使用XDocument.Load(),但我遇到了问题。 - B. Clay Shannon-B. Crow Raven
1
看起来 XmlDocument.Load() 会在头部连续指定编码时自动处理文件编码。如果没有指定,就需要处理 StreamReader 和 XmlDocument.LoadXml 等其他工具。 - CLS

16

这里的问题是myString有一个标题行。第一行可能存在某些隐藏的字符或者该行本身导致了错误。我用以下方法切掉了第一行:

xml.LoadXml(myString.Substring(myString.IndexOf(Environment.NewLine)));

这解决了我的问题。


4
有一次我遇到了一个错误,后来发现是开头有一个'?'。我只需将其替换为空格就可以运行了...如果你读取的文件编码与你所期望的不同,也可能会出现这种情况。 - Ricardo Appleton
我尝试过这个,但在.NETPrehistoric(1.1)中,我试图使用"\r\n"代替当时不可用的Environment.NewLine。结果我得到了“指定的参数超出了有效值的范围。” - B. Clay Shannon-B. Crow Raven
@Chris:我尝试了你的解决方案。我得到了以下异常。 System.ArgumentOutOfRangeException:StartIndex 不能小于零。参数 - Shesha

12

我认为问题在于编码。这就是为什么删除第一行(带有编码字节)可能会解决问题的原因。

对于“根级别的数据无效。第1行,第1个位置”的解决方法是使用以下代码替换:XDocument.Load( new MemoryStream( xmlContentInBytes ) );

我注意到我的XML字符串看起来还不错:

<?xml version="1.0" encoding="utf-8"?>

但在不同的文本编辑器编码下,它看起来像这样:

?<?xml version="1.0" encoding="utf-8"?>

最后,我不需要XML字符串,而是需要XML字节数组。如果您需要使用字符串,您应该在字符串中查找“隐形”字节,并尝试使用编码调整XML内容以进行解析或加载。

希望这能有所帮助。


4

导致此错误的主要原因是在将 Streambyte[] 数组转换为 .NET string 时确定编码的逻辑。

使用带有第二个构造函数参数 detectEncodingFromByteOrderMarks 设置为 true 的 StreamReader 将确定正确的编码并创建不会破坏 XmlDocument.LoadXml 方法的 string

public string GetXmlString(string url)
{
    using var stream = GetResponseStream(url);
    using var reader = new StreamReader(stream, true);
    return reader.ReadToEnd(); // no exception on `LoadXml`
}

一个常见的错误是在 streambyte[] 上盲目使用 UTF8 编码。下面的代码会生成 string,在 Visual Studio 调试器中检查或复制粘贴到其他地方时看起来是有效的,但如果文件的编码与 UTF8(无 BOM)不同,则在使用 LoadLoadXml 时会产生异常。

public string GetXmlString(string url)
{
    byte[] bytes = GetResponseByteArray(url);
    return System.Text.Encoding.UTF8.GetString(bytes); // potentially exception on `LoadXml`
}

4

使用不同的编码保存文件:

文件 > 另存为... > 选择“UTF-8无签名”进行保存。

在VS 2017中,您可以在保存按钮旁边的下拉菜单中找到编码选项。


1
谢谢Mike,这对我很有帮助! - Bravo

3

我通过直接编辑字节数组来解决了这个问题。 收集UTF8前缀并直接删除头部。 之后,您可以使用GetString方法将byte[]转换为字符串,如下所示。 我也删除了\r和\t,只是作为预防措施。

XmlDocument configurationXML = new XmlDocument();
List<byte> byteArray = new List<byte>(webRequest.downloadHandler.data);

foreach(byte singleByte in Encoding.UTF8.GetPreamble())
{
     byteArray.RemoveAt(byteArray.IndexOf(singleByte));
}
string xml = System.Text.Encoding.UTF8.GetString(byteArray.ToArray());
       xml = xml.Replace("\\r", "");
       xml = xml.Replace("\\t", "");

这对我有用。但在循环中,我们需要在删除之前检查byteArray.IndexOf(singleByte) != -1是否存在。 - ThanhLD

2
如果您的XML是一个字符串,请使用以下方法删除任何字节顺序标记:
        xml = new Regex("\\<\\?xml.*\\?>").Replace(xml, "");

2

起初我遇到了转义"&"字符的问题,然后发现变音符和特殊字母会显示为问号,并最终遇到了OP提到的问题。

我查看了答案并使用了@Ringo的建议尝试了Load()方法作为替代方案。这使我意识到我可以以其他方式处理响应,而不仅仅是作为字符串。

使用System.IO.Stream而不是字符串对我解决了所有问题。

var response = await this.httpClient.GetAsync(url);
var responseStream = await response.Content.ReadAsStreamAsync();
var xmlDocument = new XmlDocument();
xmlDocument.Load(responseStream);

Load() 的优点在于该方法可以自动检测输入 XML 的字符串格式(例如 UTF-8、ANSI 等等)。了解更多


0

我已经找到了其中一种解决方案。 对于你的代码,可以按照以下方式进行 -

XmlDocument xml = new XmlDocument();
try
{
    // assuming the location of the file is in the current directory 
    // assuming the file name be loadData.xml
    string myString = "./loadData.xml";
    xml.Load(myString);
}
catch (Exception ex)
{
    System.IO.File.WriteAllText(@"C:\text.txt", myString + "\r\n\r\n" + ex.Message);
    throw ex;
}

这是一个解决方案,但不是很好的一个。这是一个编码问题,通过写入和读取文件,您实际上在没有意识到的情况下执行了编码和解码操作,因为调用Load方法的重载具有Encoding参数(System.Text.Encoding encoding)的默认值。 - hardyVeles
感谢您指出这个问题,您能帮我纠正一下吗? - Shubhasish Bhunia
你应该使用编码类的方法对字符串进行解码和编码,完全没有必要(也没有意义)使用文件方法或文件系统。请查看以下链接:https://learn.microsoft.com/en-us/dotnet/api/system.text.encoding?view=netframework-4.8 - hardyVeles

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接