XmlTextReader читает не все строки в файле

197
01 августа 2018, 15:50

У меня есть XML-файл, и я хочу прочитать все строки с помощью инструментов класса XmlTextReader. Ниже приведен пример файла xml:

<?xml version="1.0" encoding="utf-8"?>
<CodeSigns>
  <CodeSign Format="1.0.0">
    <Header>
      <Title>AAA</Title>
      <Shortcut>AAA</Shortcut>
      <Description>AAA</Description>
      <Author>Viva</Author>
      <LoadTypes>
        <LoadType>Expansion</LoadType>
      </LoadTypes>
    </Header>
    <Sign>
      <Declarations />
      <Code Language="SQL Server" Kind="SQL Server"><![CDATA[SELECT TOP 100 [Id]
       ,[TotalSeconds]
       ,CAST('<![CDATA[' + [Parameters] + ']]]]><![CDATA[>' AS XML) as [Parameters]
       ,CAST('<![CDATA[' + [Query] + ']]]]><![CDATA[>' AS XML) as [Query]
       ,CAST('<![CDATA[' + [StackTrace] + ']]]]><![CDATA[>' AS XML) as [StackTrace]
       ,[CreateDate]
       ,[MachineName]
       ,[UserName]
       ,CAST('<![CDATA[' + [Exception] + ']]]]><![CDATA[>' AS XML) as [Exception]
       ,CAST('<![CDATA[' + [InnerException] + ']]]]><![CDATA[>' AS XML) as [InnerException]
       ,[CommandType]
       FROM [QMaster].[dbo].[LogSlowQueries]
       where CreateDate > CONVERT(DATE,GETDATE())
       order by totalseconds desc]]>
      </Code>
    </Sign>
  </CodeSign>
</CodeSigns>

Нам нужен элемент, чтобы прочитать все, что он включает. Остальные элементы для нас не важны, но с этим есть проблемы, потому что вместо трех были прочитаны только три строки. Код C #, используемый для чтения этого файла, приведен ниже:

public void Load(string path) {
  filename = Path.GetFileName(path);
  try {
    using (XmlTextReader reader = new XmlTextReader(path)) {
      while (reader.Read()) {
        if (reader.NodeType == XmlNodeType.Element) {
          switch (reader.Name) {
            case "Title":
              title = ReadProperty(reader);
              break;
            case "ToolTip":
              tooltip = ReadProperty(reader);
              break;
            case "Description":
              description = ReadProperty(reader);
              break;
            case "Author":
              author = ReadProperty(reader);
              break;                
            **case "Code":
              language = reader.GetAttribute("Language");
              code = ReadProperty(reader);
              break;**                
          }
        }
      }
    }       
  }
  catch (XmlException) {
  }
}


private string ReadProperty(XmlReader reader) {
  if (reader.IsEmptyElement)
    return string.Empty;
  reader.Read();
  return reader.Value;
}

Нам нужно получить все строки из XmlNodeType.Element с именем «Code». Это должно быть прочитано все со всеми строками в этом. Однако мы получаем в reader.Value только три строки на картинке:

Большое спасибо за ответы

Answer 1

Во-первых, поговорим о форматировании.

Если xml отформатирован именно так, как показано в вопросе:

<Code Language="SQL Server" Kind="SQL Server"><![CDATA[SELECT TOP 100 [Id]

т. е. содержимое элемента Code начинается в этой же строке, тогда результат будет как у вас.

Однако, если форматирование будет таким:

<Code Language="SQL Server" Kind="SQL Server">
  <![CDATA[SELECT TOP 100 [Id]

т. е. содержимое начинается со следующей строки, то ваш код вернёт строку, состоящую из пробелов. А именно такой формат получился, когда я вставил ваш xml в редактор Visual Studio (Студия сама его отформатировала).

Вы обязаны проверять в методе ReadProperty, до какого узла дошли, а не просто слепо полагаться на жёстко-заданный формат. Потому что в соответствии со стандартом, xml имеет право быть отформатированным как угодно.

Свойство reader.Value возвращает содержимое текущего узла. Этим узлом может быть не только Element, как вероятно вы ожидали, но и CDATA, и Whitespace. Соответственно, возвращается либо содержимое первого блока CDATA (три строки текста), либо пробельная строка.

Во-вторых, блоки CDATA не могут быть вложенными. Посмотрите, как их подсвечивает xml-редактор Visual Studio:

По-хорошему каждый из этих блоков нужно закрывать скобкой ]]> перед началом следующего. Или, что проще и разумнее (на мой взгляд), оставить один-единственный блок CDATA.

Ваш метод ReadProperty можно заменить одной строкой:

reader.ReadElementContentAsString()

При этом будет прочитано всё содержимое элемента Code как одно целое. При этом текст будет извлечён из CDATA. Но только из тех блоков, которые парсер распознает как самостоятельные (не вложенные)! См. выше картинку.

Что делать с оставшимися кусками блоков CDATA? Проблему нужно решать уровнем выше: при записи/создании xml.

Возможно, вас устроит чтение без извлечения из блоков CDATA. Для этого используйте

var code = reader.ReadInnerXml();

Напоследок замечу, что класс XmlRextReader считается устаревшим, как и написано в документации.

Поэтому я бы заменил строку

using (XmlTextReader reader = new XmlTextReader(path))

на следующую

using (var reader = XmlReader.Create(path))

Но это соверщенно не важно.

Answer 2

Решение найдено, все работает:

private string ReadProperty(XmlReader reader) {
  if (reader.IsEmptyElement)
    return string.Empty;
  reader.Read();
  var st1 = reader.Value;
  while (reader.Read()) {
    if (reader.NodeType == XmlNodeType.CDATA)
      st1 += reader.Value;
  }
  return st1;
}
READ ALSO
Вставка картинки в текст

Вставка картинки в текст

Нужно вставлять картинки за место слов в тексте в RichTextBox, соответственно что бы обтекание было слева и справа

148
Как отследить событие со всех CheckBox?

Как отследить событие со всех CheckBox?

Имеется вот такая панель кнопок, в ScrollViewer1 вложен StackPanel с сотней кнопок, на каждой кнопке SteckPanel с Image, CheckBox и TextBlock, CheckBox управляет видимостью...

159
Как соединится с базой данных opencart

Как соединится с базой данных opencart

На сайте есть скрипт обработчик, при попытке соединится через mysql_connect ничего не получаетсяСкрипт лежит просто в корневой папке

193
Рассылка почты из php скрипта через smtp

Рассылка почты из php скрипта через smtp

Здравствуйте, решил сделать рассылку писем через smtp, используя данный скрипт на php:http://i-leonru/smtp-php/

181