Обработка плохо отформатированного XML в.NET3.5
Учитывая стороннюю систему, которая передает мне XML через TCP. ВСЕГО передаваемый XML-контент (не одно сообщение потока, а объединенные сообщения) выглядит следующим образом:
<root>
<insert ....><remark>...</remark></insert>
<delete ....><remark>...</remark></delete>
<insert ....><remark>...</remark></insert>
....
<insert ....><remark>...</remark></insert>
</root>
Каждая строка из приведенного выше образца обрабатывается индивидуально. Поскольку это потоковый процесс, я не могу просто ждать, пока все придет, я должен обрабатывать контент по мере поступления. Проблема в том, что фрагменты контента могут быть разрезаны в любой точке, теги не соблюдаются. У вас есть хороший совет о том, как обрабатывать контент, если он поступает такими фрагментами?
Кусок 1:
<root>
<insert ....><rem
Кусок 2:
ark>...</remark></insert>
<delete ....><remark>...</remark></delete>
<insert ....><remark>...</rema
Чанк N:
rk></insert>
....
<insert ....><remark>...</remark></insert>
</root>
РЕДАКТИРОВАТЬ:
Хотя скорость обработки не имеет значения (никаких проблем в реальном времени), я не могу дождаться полного сообщения. Практически последний кусок никогда не прибывает. Сторонняя система отправляет сообщения всякий раз, когда она сталкивается с изменениями. Процесс никогда не заканчивается, это поток, который никогда не останавливается.
2 ответа
После дальнейшего исследования мы выяснили, что поток XML был разрезан буфером TCP, когда он был заполнен. Таким образом, нарезка происходит фактически случайно в потоке байтов, вызывая сокращения даже внутри символов Юникода. Поэтому мы должны были собрать части на уровне байтов и преобразовать их обратно в текст. Если разговор потерпит неудачу, мы подождали следующий байт и попытались снова.
Моя первая мысль об этой проблеме - создать простую производную TextReader, которая отвечает за буферизацию ввода из потока. Этот класс затем будет использоваться для подачи XmlReader. Производная TextReader может довольно легко сканировать входящий контент в поисках полных "блоков" XML (полный элемент с начальными и конечными скобками, фрагмент текста, полный атрибут и т. Д.). Он также может предоставить флаг вызывающему коду, чтобы указать, когда один или несколько "блоков" доступны, поэтому он может запросить следующий узел XML из XmlReader, который будет инициировать отправку этого блока из производной TextReader и удаление его из буфера.,
Изменить: Вот быстрый и грязный пример. Я понятия не имею, работает ли это отлично (я не проверял это), но это сталкивается с идеей, которую я пытался передать.
public class StreamingXmlTextReader : TextReader
{
private readonly Queue<string> _blocks = new Queue<string>();
private string _buffer = String.Empty;
private string _currentBlock = null;
private int _currentPosition = 0;
//Returns if there are blocks available and the XmlReader can go to the next XML node
public bool AddFromStream(string content)
{
//Here is where we would can for simple blocks of XML
//This simple chunking algorithm just uses a closing angle bracket
//Not sure if/how well this will work in practice, but you get the idea
_buffer = _buffer + content;
int start = 0;
int end = _buffer.IndexOf('>');
while(end != -1)
{
_blocks.Enqueue(_buffer.Substring(start, end - start));
start = end + 1;
end = _buffer.IndexOf('>', start);
}
//Store the leftover if there is any
_buffer = end < _buffer.Length
? _buffer.Substring(start, _buffer.Length - start) : String.Empty;
return BlocksAvailable;
}
//Lets the caller know if any blocks are currently available, signaling the XmlReader can ask for another node
public bool BlocksAvailable { get { return _blocks.Count > 0; } }
public override int Read()
{
if (_currentBlock != null && _currentPosition < _currentBlock.Length - 1)
{
//Get the next character in this block
return _currentBlock[_currentPosition++];
}
if(BlocksAvailable)
{
_currentBlock = _blocks.Dequeue();
_currentPosition = 0;
return _currentBlock[0];
}
return -1;
}
}