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 = DtdProcessin开发者_Go百科g.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
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(); }
}
I'm not sure if this is elegant but it worked for me. I am unsure what is going on though, if you google the precise error message this is the only result!
Code that throws up:
WebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Timeout = (int)_Timeout.TotalMilliseconds;
using (var resp = (HttpWebResponse)request.GetResponse())
{
using (XmlReader reader = XmlTextReader.Create(resp.GetResponseStream()))
{
reader.Read();
return (XElement)XElement.ReadFrom(reader);
}
}
Code that is happy:
WebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Timeout = (int)_Timeout.TotalMilliseconds;
using (var resp = (HttpWebResponse)request.GetResponse())
{
using (var responseStream = resp.GetResponseStream())
{
using (var reader = new StreamReader(responseStream, Encoding.ASCII))
{
string raw = reader.ReadToEnd();
return (XElement)XElement.Parse(raw);
}
}
}
So for some reason reading the response into a string and passing that to the Parse() function does something different to using the ReadFrom() function and a stream. Potentially the Parse function is a bit more loose, but I have not dug further.
精彩评论