Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

InvalidOperationException on wp7 reading XML with no CR between xml declaration and doctype

I'm loading XML on WP7, and I find that if I don't have a newline between the XML declaration and the doctype, even though I am ignoring the doctype, I get an InvalidOperationException. On the desktop I get no such error.

My code:

private static void Example()
{
    const string works =
        @"<?xml version=""1.0""?>
<!DOCTYPE example SYSTEM ""http://example.com/example.dtd""><hello></hello>";

    const string fails =
        @"<?xml version=""1.0""?><!DOCTYPE example SYSTEM ""http://example.com/example.dtd""><hello></hello>";

    var textReader = new StringReader(works);  
    var xmlReaderSettings = new XmlReaderSettings {DtdProcessing = DtdProcessing.Ignore,};
    var xmlReader = XmlReader.Create(textReader, xmlReaderSettings);
    XDocument.Load(xmlReader);  // No problem here

    textReader = new StringReader(fails);  

    xmlReader = XmlReader.Create(textReader, xmlReaderSettings);
    XDocument.Load(xmlReader);  // Fails here
}

The second XDocument.Load fails with an InvalidOperationException and the message The XmlReader should not be on a node of type XmlDeclaration. The only difference is the lack of a new line in the second case.

Has anyone seen this before, and found a workaround? This works on the desktop btw - just fails on WP7. In my real case I am reading the XML from a stream, so it won't be so easy to manually inject the new line at the right place.

Damian

like image 801
Damian Avatar asked May 05 '11 19:05

Damian


1 Answers

For now I've implemented a TextReader wrapper that injects the NewLine. I'm including it here in case someone finds it useful, and also in case someone has a more elegant solution - if so please comment!

It relies on the XmlReader only calling the Read(...) method to read data - otherwise it throws a NotImplementedException.

In the above example you'd use it like this:

textReader = new NewlineAfterXmlDeclReader(new StringReader(fails));  

This is the implementation

class NewlineAfterXmlDeclReader : TextReader
{
    private const int InitialChunkSize = 80;
    private const string SearchText = "?><!" + "DOCTYPE";  //concatenation injected for readability in SO purposes only
    private static readonly string ReplaceText = "?>" + Environment.NewLine + "<!" + "DOCTYPE";

    private readonly TextReader _wrappedReader;
    private TextReader _firstChunkReader;

    public NewlineAfterXmlDeclReader(TextReader wrappedReader)
    {
        _wrappedReader = wrappedReader;
        var initialChunk = new char[InitialChunkSize];
        var count = _wrappedReader.Read(initialChunk, 0, InitialChunkSize);
        var initialChunkString = new String(initialChunk, 0, count);

        _firstChunkReader = new StringReader(initialChunkString.Replace(SearchText, ReplaceText));
    }

    public override int Read(char[] buffer, int index, int count)
    {
        var firstChunkReadCount = 0;
        if (_firstChunkReader != null)
        {
            firstChunkReadCount = _firstChunkReader.ReadBlock(buffer, index, count);
            if (firstChunkReadCount == count) return firstChunkReadCount;
            _firstChunkReader = null;
            index += firstChunkReadCount;
            count -= firstChunkReadCount;
        }

        return firstChunkReadCount + _wrappedReader.Read(buffer, index, count);
    }

    public override void Close()
    {
        _wrappedReader.Close();
    }

    protected override void Dispose(bool disposing)
    {
        _wrappedReader.Dispose();
    }

    public override int Peek() { throw new NotImplementedException(); }
    public override int Read() { throw new NotImplementedException(); }
    public override string ReadToEnd() { throw new NotImplementedException(); }
    public override int ReadBlock(char[] buffer, int index, int count) { throw new NotImplementedException(); }
    public override string ReadLine() { throw new NotImplementedException(); }
}
like image 154
Damian Avatar answered Sep 28 '22 19:09

Damian