Читать все элементы из потока XML-сети - PullRequest
6 голосов
/ 21 февраля 2012

Я пишу сетевой сервер на C # .NET 4.0. Существует сетевое соединение TCP / IP, по которому я могу получить полные элементы XML. Они прибывают регулярно, и мне нужно немедленно их обработать. Каждый элемент XML сам по себе является полным документом XML, поэтому он имеет открывающий элемент, несколько подузлов и закрывающий элемент. Нет единого корневого элемента для всего потока. Поэтому, когда я открываю соединение, я получаю следующее:

<status>
    <x>123</x>
    <y>456</y>
</status>

Затем, через некоторое время, оно продолжается:

<status>
    <x>234</x>
    <y>567</y>
</status>

И так далее. Мне нужен способ прочитать полную строку XML, пока элемент состояния не будет завершен. Я не хочу делать это с помощью методов чтения простого текста, потому что я не знаю, в каком формате поступают данные. Я никоим образом не могу ждать, пока весь поток не закончится, как это часто описывается в другом месте. Я пытался использовать класс XmlReader, но его документация странная, методы не работают, первый элемент теряется, и после отправки второго элемента возникает исключение XmlException, потому что есть два корневых элемента.

Ответы [ 5 ]

7 голосов
/ 21 февраля 2012

Попробуйте это:

var settings = new XmlReaderSettings
{
    ConformanceLevel = ConformanceLevel.Fragment
};

using (var reader = XmlReader.Create(stream, settings))
{
    while (!reader.EOF)
    {
        reader.MoveToContent();

        var doc = XDocument.Load(reader.ReadSubtree());

        Console.WriteLine("X={0}, Y={1}",
            (int)doc.Root.Element("x"),
            (int)doc.Root.Element("y"));

        reader.ReadEndElement();
    }
}
1 голос
/ 21 февраля 2012

Вы можете использовать XElement.Load, что больше предназначено для потоковой передачи фрагментов элемента XML, нового в .net 3.5 и также поддерживающего чтение непосредственно из потока.

Взгляните на System.Xml.Linq

Я думаю, что вам, возможно, еще придется добавить некоторую логику управления, чтобы разделить получаемые сообщения, но вы также можете попробовать.

1 голос
/ 21 февраля 2012

Если вы измените «уровень соответствия» на «фрагмент», он может работать с XmlReader.

Это (слегка измененный) пример из MSDN :

XmlReaderSettings settings = new XmlReaderSettings();
settings.ConformanceLevel = ConformanceLevel.Fragment;
XmlReader reader = XmlReader.Create(streamOfXmlFragments, settings);
0 голосов
/ 27 марта 2017

Существенно не отличается от решения dtb, но linqier

static IEnumerable<XDocument> GetDocs(Stream xmlStream)
{
    var xmlSettings = new XmlReaderSettings() { ConformanceLevel = ConformanceLevel.Fragment };
    using (var xmlReader = XmlReader.Create(xmlStream, xmlSettings))
    {
        var xmlPathNav = new XPathDocument(xmlReader).CreateNavigator();
        foreach (var selectee in xmlPathNav.Select("/*").OfType<XPathNavigator>())
            yield return XDocument.Load(selectee.ReadSubtree());
    }
}

Я столкнулся с подобной проблемой в PowerShell, но вопрос задающего был в C #, поэтому я попытался перевести ее (и проверил, чтооно работает). Здесь - это то место, где я нашел подсказку, которая заставила меня преодолеть последние маленькие неровности (".... Способ, которым XPathDocument делает свое волшебство, заключается в создании« прозрачного »корневого узла и удержании фрагментов из него.Я говорю, что это прозрачно, потому что ваши запросы XPath могут использовать ось корневого узла и все еще правильно разрешаться для фрагментов ... ")

Фрагменты XML, с которыми я работаю, оказываются небольшими.Если бы у вас были большие куски, вы, вероятно, захотите взглянуть на XStreamingElement - это может добавить много сложности, но также значительно уменьшить использование памяти при работе с большими объемами XML.

0 голосов
/ 21 февраля 2012

Я не уверен, что есть что-то встроенное, что делает это.Я бы открывал построитель строк, заполнял его до тех пор, пока не увидел тег </status>, а затем анализировал его с помощью обычного XmlDocument.

...