Search code examples
c#xmlstreamreaderreadlinexmltextreader

Why would StreamReader.ReadLine do a better job of reading XML than XmlTextReader.Read?


I have this code which works fine from a Winforms app calling a Web API app (it successfully copies the XML file over to the server):

public static string SendXMLFile(string xmlFilepath, string uri, int timeout)
{
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);

    request.KeepAlive = false;
    request.ProtocolVersion = HttpVersion.Version10;
    request.ContentType = "application/xml";
    request.Method = "POST";

    StringBuilder sb = new StringBuilder();
    using (StreamReader sr = new StreamReader(xmlFilepath))
    {
        String line;
        while ((line = sr.ReadLine()) != null)
        {
            sb.AppendLine(line);
        }
        MessageBox.Show(sb.ToString());
        byte[] postBytes = Encoding.UTF8.GetBytes(sb.ToString());

        if (timeout < 0)
        {
            request.ReadWriteTimeout = timeout;
            request.Timeout = timeout;
        }

        request.ContentLength = postBytes.Length;

        try
        {
            Stream requestStream = request.GetRequestStream();

            requestStream.Write(postBytes, 0, postBytes.Length);
            requestStream.Close();

            HttpWebResponse response = null; //<= uncomment for older versions of .NET
            try
            {
                response = (HttpWebResponse)request.GetResponse(); //<= uncomment for older versions of .NET (comment out using)
            }
            finally
            {
                IDisposable disposableResponse = response as IDisposable;
                if (disposableResponse != null) disposableResponse.Dispose();
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
            request.Abort();
            return string.Empty;
        }
    }
}

However, when trying to figure out why code that is as similar as possible to this working code is does not work when trying to send an xml file from a handheld device to the same server, I tried using XmlTextReader instead (as was suggested here ("You are not using XML classes and methods here"), where I go into detail about my travails with that); the only difference is that this:

using (StreamReader sr = new StreamReader(xmlFilepath))
{
    String line;
    while ((line = sr.ReadLine()) != null)
    {
        sb.AppendLine(line);
    }
    MessageBox.Show(sb.ToString());
    byte[] postBytes = Encoding.UTF8.GetBytes(sb.ToString());

...in the working (StreamReader) code was changed to this:

XmlTextReader reader = new XmlTextReader(xmlFilepath);
while (reader.Read())
{
    switch (reader.NodeType)
    {
        case XmlNodeType.Element: // The node is an Element.
            sb.Append("<" + reader.Name);

            while (reader.MoveToNextAttribute()) // Read attributes.
                sb.Append(" " + reader.Name + "='" + reader.Value + "'");
            sb.Append(">");
            sb.Append(">");
            break;
        case XmlNodeType.Text: //Display the text in each element.
            sb.Append(reader.Value);
            break;
        case XmlNodeType.EndElement: //Display end of element.
            sb.Append("</" + reader.Name);
            sb.Append(">");
            break;
    }
}

MessageBox.Show(sb.ToString());
byte[] postBytes = Encoding.UTF8.GetBytes(sb.ToString());

With the StreamReader code, the read data looks just as it does on disk:

enter image description here

With the XmlTextReader, though, it's all jumbled up like jambalaya shot out of a cannon at a herd of hippos:

enter image description here

...and the XmlTextReader version fails, with a "The remote server returned an error: (500) Internal Server Error."

So StreamReader.ReadLine works better with an XML file than XmlTextReader.Read does; seems strange...am I doing something wrong here?


Solution

  • By default the the XmlTextReader will ignore whitespace. You'll want to tell it specifically to include whitespace and also start to process whitespace in your switch.

    Also note that all XmlReader implementations are disposable so wrap them in a using.

    XmlReaderSettings settings = new XmlReaderSettings();
    settings.IgnoreWhitespace = false;
    using(XmlReader reader = XmlReader.Create(xmlFilepath, settings)) {
        while (reader.Read())
        {
            switch (reader.NodeType)
            {
                case XmlNodeType.Element: // The node is an Element.
                    sb.Append("<" + reader.Name);
    
                    while (reader.MoveToNextAttribute()) // Read attributes.
                        sb.Append(" " + reader.Name + "='" + reader.Value + "'");
                    sb.Append(">");
                    sb.Append(">");
                    break;
                case XmlNodeType.Text: 
                case XmlNodeType.Whitespace:
                    sb.Append(reader.Value);
                    break;
                case XmlNodeType.EndElement: //Display end of element.
                    sb.Append("</" + reader.Name);
                    sb.Append(">");
                    break;
            }
        }
    }
    MessageBox.Show(sb.ToString());
    byte[] postBytes = Encoding.UTF8.GetBytes(sb.ToString());