У меня есть 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 только три строки на картинке:
Большое спасибо за ответы
Во-первых, поговорим о форматировании.
Если 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))
Но это соверщенно не важно.
Решение найдено, все работает:
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;
}
Апостиль в Лос-Анджелесе без лишних нервов и бумажной волокиты
Основные этапы разработки сайта для стоматологической клиники
Продвижение своими сайтами как стратегия роста и независимости