Буфер XmlReader, кажется, игнорирует изменения в буфере?
Возможно, мое понимание того, что должно произойти, неверно, так что, надеюсь, кто-то сможет исправить мой мыслительный процесс здесь.
Я пытаюсь обработать много больших XML-файлов, которые постоянно отправляются нам с плохими символами, встроенными в текст (0x1A)... к сожалению, наш клиент отправляет файлы, так что как бы мы ни просили их сделать файлы на самом деле являются действительными XML, они считают это нашей проблемой.
В конце концов я написал подкласс StreamReader
вот так:
public class CleanTextReader : StreamReader
{
private readonly ILog _logger;
public CleanTextReader(Stream stream, ILog logger) : base(stream)
{
this._logger = logger;
}
public CleanTextReader(Stream stream) : this(stream, LogManager.GetLogger<CleanTextReader>())
{
//nothing to do here.
}
public override int Read(char[] buffer, int index, int count)
{
try
{
var rVal = base.Read(buffer, index, count);
var filteredBuffer = buffer.Select(x => XmlConvert.IsXmlChar(x) ? x : ' ').ToArray();
Buffer.BlockCopy(filteredBuffer, 0, buffer, 0, count);
return rVal;
}
catch (Exception ex)
{
this._logger.Error("Read(char[], int, int)", ex);
throw;
}
}
public override int ReadBlock(char[] buffer, int index, int count)
{
try
{
var rVal = base.ReadBlock(buffer, index, count);
var filteredBuffer = buffer.Select(x => XmlConvert.IsXmlChar(x) ? x : ' ').ToArray();
Buffer.BlockCopy(filteredBuffer, 0, buffer, 0, count);
return rVal;
}
catch (Exception ex)
{
this._logger.Error("ReadBlock(char[], in, int)", ex);
throw;
}
}
public override string ReadToEnd()
{
var chars = new char[4096];
int len;
var sb = new StringBuilder(4096);
while ((len = Read(chars, 0, chars.Length)) != 0)
{
sb.Append(chars, 0, len);
}
return sb.ToString();
}
}
... тогда я реализую XmlReader
вот так:
using (var theCleanser = new CleanTextReader(myStreamedInput))
using (var theReader = XmlReader.Create(theCleanser))
{
...
// do stuff with theReader
}
У меня есть модульный тест, как так:
[TestMethod]
public void CleanTextReaderCleans0X1A()
{
//arrange
var originalString = "The quick brown fox jumped over the lazy dog.";
var badChars = new string(new[] {(char) 0x1a});
var concatenated = originalString.Replace("jumped", badChars + "jumped" + badChars);
//act
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(concatenated)))
{
using (var reader = new CleanTextReader(stream))
{
var newString = reader.ReadToEnd().Trim().Replace(" ", " ");
//assert
Assert.IsTrue(originalString.Equals(newString));
}
}
}
... это проходит.
но когда я пытаюсь разобрать файл XML с символом 0x1A, я все равно получаю System.Xml.XmlException
: '', шестнадцатеричное значение 0x1A, является недопустимым символом. Строка XX, позиция XX
Копаем глубже в CleanTextReader
Я изучаю Read(char[], int, int)
метод, как кажется, поражен XmlReader
, Оригинал buffer
имеет недопустимые символы, но filteredBuffer
нет, и после Buffer.BlockCopy()
работает, оба buffer
и filteredBuffer
лишены специальных символов.
Также следует отметить, что я обнаружил, что номер строки и позиция ссылаются не на первый экземпляр недопустимого символа, а на второй, поэтому он видит первый и исправляет его, но только один раз.
Так что я чешу голову здесь. Как работает XmlReader
получить специальные символы? Это чтение из буфера до того, как управление вернется из метода? Как мне исправить эту проблему?
ОБНОВИТЬ
по запросу я получаю трассировку стека:
"System.Xml.XmlException: '', hexadecimal value 0x1A, is an invalid character. Line 84, position 38.
at System.Xml.XmlTextReaderImpl.Throw(Exception e)
at System.Xml.XmlTextReaderImpl.Throw(String res, String[] args)
at System.Xml.XmlTextReaderImpl.ParseText(Int32& startPos, Int32& endPos, Int32& outOrChars)
at System.Xml.XmlTextReaderImpl.ParseText()
at System.Xml.XmlTextReaderImpl.ParseElementContent()
at System.Xml.XmlTextReaderImpl.Read()
at System.Xml.Linq.XContainer.ReadContentFrom(XmlReader r)
at System.Xml.Linq.XContainer.ReadContentFrom(XmlReader r, LoadOptions o)
at System.Xml.Linq.XElement.ReadElementFrom(XmlReader r, LoadOptions o)
at System.Xml.Linq.XNode.ReadFrom(XmlReader reader)
at MyCompany.Importers.GroupEligibilityModel.Loader.<GetGroupEligibilityElements>d__2b.MoveNext() in c:\\Projects\\MyCompanyHealth\\MyCompany.Importers\\MyCompany.Importers.GroupEligibilityModel\\MyCompany.Importers.GroupEligibilityModel\\Loader.cs:line 138
at MyCompany.Importers.GroupEligibilityModel.Loader.<GetGroupEligibilities>d__18.MoveNext() in c:\\Projects\\MyCompanyHealth\\MyCompany.Importers\\MyCompany.Importers.GroupEligibilityModel\\MyCompany.Importers.GroupEligibilityModel\\Loader.cs:line 71
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at MyCompany.Importers.GroupEligibilityModel.Test.LoadingTests.GroupEligibilityFileWithBadCharactersProperlyCleansed() in c:\\Projects\\MyCompanyHealth\\MyCompany.Importers\\MyCompany.Importers.GroupEligibilityModel\\MyCompany.Importers.GroupEligibilityModel.Test\\LoadingTests.cs:line 118" string