66

Does anyone have any idea why the following code sample fails with an XmlException "Data at the root level is invalid. Line 1, position 1."

var body = "<?xml version="1.0" encoding="utf-16"?><Report> ......"
XmlDocument bodyDoc = new XmlDocument();            
bodyDoc.LoadXml(body);
Ant Swift
  • 20,089
  • 10
  • 38
  • 55
  • 1
    Dan is right - the code is fine. One quick and easy way to check the xml is to open it in Internet Explorer – David Hall Nov 22 '08 at 01:22
  • 1
    Are you sure that you have the encoding in the body set to utf-16 and not something else? Also, is your body string actually escaped, e.g. body = "\nThis is a Test"; – Zach Burlingame Jun 23 '09 at 16:36

9 Answers9

121

Background

Although your question does have the encoding set as UTF-16, you don't have the string properly escaped so I wasn't sure if you did, in fact, accurately transpose the string into your question.

I ran into the same exception:

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

However, my code looked like this:

string xml = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<event>This is a Test</event>";
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xml);

The Problem

The problem is that strings are stored internally as UTF-16 in .NET however the encoding specified in the XML document header may be different. E.g.:

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

From the MSDN documentation for String here:

Each Unicode character in a string is defined by a Unicode scalar value, also called a Unicode code point or the ordinal (numeric) value of the Unicode character. Each code point is encoded using UTF-16 encoding, and the numeric value of each element of the encoding is represented by a Char object.

This means that when you pass XmlDocument.LoadXml() your string with an XML header, it must say the encoding is UTF-16. Otherwise, the actual underlying encoding won't match the encoding reported in the header and will result in an XmlException being thrown.

The Solution

The solution for this problem is to make sure the encoding used in whatever you pass the Load or LoadXml method matches what you say it is in the XML header. In my example above, either change your XML header to state UTF-16 or to encode the input in UTF-8 and use one of the XmlDocument.Load methods.

Below is sample code demonstrating how to use a MemoryStream to build an XmlDocument using a string which defines a UTF-8 encode XML document (but of course, is stored a UTF-16 .NET string).

string xml = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<event>This is a Test</event>";

// Encode the XML string in a UTF-8 byte array
byte[] encodedString = Encoding.UTF8.GetBytes(xml);

// Put the byte array into a stream and rewind it to the beginning
MemoryStream ms = new MemoryStream(encodedString);
ms.Flush();
ms.Position = 0;

// Build the XmlDocument from the MemorySteam of UTF-8 encoded bytes
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(ms);
Mo Patel
  • 2,321
  • 4
  • 22
  • 37
Zach Burlingame
  • 13,476
  • 14
  • 56
  • 65
33

Simple and effective solution: Instead of using the LoadXml() method use the Load() method

For example:

XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("sample.xml");
Yi Jiang
  • 49,435
  • 16
  • 136
  • 136
Gunner
  • 339
  • 3
  • 2
8

I figured it out. Read the MSDN documentation and it says to use .Load instead of LoadXml when reading from strings. Found out this works 100% of time. Oddly enough using StringReader causes problems. I think the main reason is that this is a Unicode encoded string and that could cause problems because StringReader is UTF-8 only.

MemoryStream stream = new MemoryStream();
            byte[] data = body.PayloadEncoding.GetBytes(body.Payload);
            stream.Write(data, 0, data.Length);
            stream.Seek(0, SeekOrigin.Begin);

            XmlTextReader reader = new XmlTextReader(stream);

            // MSDN reccomends we use Load instead of LoadXml when using in memory XML payloads
            bodyDoc.Load(reader);
  • 1
    Reading the MSDN documentation for XmlDocument.LoadXml(String) here: http://msdn.microsoft.com/en-us/library/system.xml.xmldocument.loadxml.aspx the method summary states: "Loads the XML document from the specified string." However, as you stated, it Does say: "If you want to load from a Stream, String, TextReader, or XmlReader, use the Load method instead of this method." Additionally, if you look at XmlDocument.Load(String) it says: URL for the file containing the XML document to load. "The URL can be either a local file or an HTTP URL (a Web address)." (more coming in another comment) – Zach Burlingame Jun 23 '09 at 16:07
  • 4
    I believe the intended purpose of the line "If you want to load from a Stream, String, ..." is actually "If you want to load from a Stream, File, ..." but they have String because loading from a File takes a string parameter for the filename. I don't believe their intention is "if you want to load an XmlDocument from an in-memory string, use Load". After all, that is the stated purpose of LoadXml(String)! While your solution does provide a work around, I don't think it addresses the actual stated problem (which I'm having as well) with XmlDocument.LoadXml(String). – Zach Burlingame Jun 23 '09 at 16:12
7

Try this:

XmlDocument bodyDoc = new XmlDocument();
bodyDoc.XMLResolver = null;
bodyDoc.Load(body);
sth
  • 222,467
  • 53
  • 283
  • 367
3

This worked for me:

var xdoc = new XmlDocument { XmlResolver = null };  
xdoc.LoadXml(xmlFragment);
Yi Jiang
  • 49,435
  • 16
  • 136
  • 136
keithl8041
  • 2,383
  • 20
  • 27
2

This really saved my day.

I have written a extension method based on Zach's answer, also I have extended it to use the encoding as a parameter, allowing for different encodings beside from UTF-8 to be used, and I wrapped the MemoryStream in a 'using' statement.

public static class XmlHelperExtentions
{
    /// <summary>
    /// Loads a string through .Load() instead of .LoadXml()
    /// This prevents character encoding problems.
    /// </summary>
    /// <param name="xmlDocument"></param>
    /// <param name="xmlString"></param>
    public static void LoadString(this XmlDocument xmlDocument, string xmlString, Encoding encoding = null) {

        if (encoding == null) {
            encoding = Encoding.UTF8;
        }

        // Encode the XML string in a byte array
        byte[] encodedString = encoding.GetBytes(xmlString);

        // Put the byte array into a stream and rewind it to the beginning
        using (var ms = new MemoryStream(encodedString)) {
            ms.Flush();
            ms.Position = 0;

            // Build the XmlDocument from the MemorySteam of UTF-8 encoded bytes
            xmlDocument.Load(ms);
        }
    }
}
1

I had the same problem when switching from absolute to relative path for my xml file. The following solves both loading and using relative source path issues. Using a XmlDataProvider, which is defined in xaml (should be possible in code too) :

    <Window.Resources>
    <XmlDataProvider 
        x:Name="myDP"
        x:Key="MyData"
        Source=""
        XPath="/RootElement/Element"
        IsAsynchronous="False"
        IsInitialLoadEnabled="True"                         
        debug:PresentationTraceSources.TraceLevel="High"  /> </Window.Resources>

The data provider automatically loads the document once the source is set. Here's the code :

        m_DataProvider = this.FindResource("MyData") as XmlDataProvider;
        FileInfo file = new FileInfo("MyXmlFile.xml");

        m_DataProvider.Document = new XmlDocument();
        m_DataProvider.Source = new Uri(file.FullName);
Rubarb
  • 85
  • 2
0

Simple line:

bodyDoc.LoadXml(new MemoryStream(Encoding.Unicode.GetBytes(body)));

xadriel
  • 191
  • 2
  • 6
0

I had the same issue because the XML file I was uploading was encoded using UTF-8-BOM (UTF-8 byte-order mark).

Switched the encoding to UTF-8 in Notepad++ and was able to load the XML file in code.

Hugh
  • 11
  • 1