ФОН
Я получаю много XML-файлов, которые не содержат символов новой строки, и для их быстрого форматирования я использую функцию ниже.
СЦЕНАРИЙ
Когда я запускаю инструмент в первый раз для файла, который не содержит символов новой строки
(и никаких незначительных пробелов), тогда все работает как положено:
Convert("myfile.xml", " ");
Если я снова запущу инструмент для того же файла, который только что отформатировал, чтобы увеличить отступ, отступ не изменится:
Convert("myfile.xml", " ");
ВОПРОС
Почему файл не форматируется при втором запуске функции? Как мне убедиться, что функция всегда форматирует файл?
public static void Convert(string filename, string indent)
{
var input_string = File.ReadAllText(filename, Encoding.UTF8);
var settings = new XmlWriterSettings
{
NewLineHandling = NewLineHandling.Entitize,
Indent = true,
IndentChars = indent,
NewLineChars = Environment.NewLine
};
var sb = new StringBuilder();
using (var reader = XmlReader.Create(new StringReader(input_string)))
using (var writer = XmlWriter.Create(sb, settings))
{
writer.WriteNode(reader, false);
writer.Close();
}
File.Delete(filename);
Encoding utf8 = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);
File.WriteAllText(filename, sb.ToString(), utf8);
}
ПРИМЕЧАНИЕ
Если я модифицирую программу чтения так, чтобы она игнорировала пробелы, писатель сможет правильно отформатировать вывод:
XmlReader.Create(new StringReader(input_string),
new XmlReaderSettings { IgnoreWhitespace = true })
Но мне все еще интересно, почему автору не удается отформатировать вывод, если между тегами есть незначительные пробелы.
Проблема, с которой вы столкнулись, заключается в том, что вы удаляете исходный файл перед записью в него отформатированного XML. Когда вы удаляете исходный файл, вы по сути удаляете источник, из которого считываете данные XML, поэтому при повторной попытке его форматирования источник оказывается пустым, и функция не будет работать должным образом.
public static void Convert(string filename, string indent)
{
var input_string = File.ReadAllText(filename, Encoding.UTF8);
var settings = new XmlWriterSettings
{
NewLineHandling = NewLineHandling.Entitize,
Indent = true,
IndentChars = indent,
NewLineChars = Environment.NewLine
};
var sb = new StringBuilder();
using (var reader = XmlReader.Create(new StringReader(input_string)))
using (var writer = XmlWriter.Create(sb, settings))
{
writer.WriteNode(reader, false);
writer.Close();
}
string tempFileName = Path.GetTempFileName();
Encoding utf8 = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);
File.WriteAllText(tempFileName, sb.ToString(), utf8);
File.Copy(tempFileName, filename, true);
File.Delete(tempFileName);
}
OP считывает содержимое исходного файла в строку задолго до его удаления, поэтому удаление исходного файла не представляет проблемы. Ваш код показывает точно такие же результаты.
Удаление не требуется, поскольку File.WriteAllText() перезаписывает существующий файл. Я добавил его для удобства чтения, чтобы читателю не приходилось его искать.
Проблема в том, что если читатель сохраняет незначительные пробелы, то для автора это теперь значительные пробелы.
Таким образом, он не может добавлять больше пробелов, так как это изменит смысл, или, по крайней мере, похоже, он не проверяет, что записываемый внутренний текст представляет собой только пробелы.
Таким образом, правильное решение — сначала удалить пробелы и переписать их, используя упомянутый вами код new XmlReaderSettings { IgnoreWhitespace = true })
Кстати, эффективнее просто передавать потоки, а не использовать строки и построители строк. Я ценю, что вы перезаписываете файл, поэтому вам нужно поместить существующий в массив байтов.
var input = File.ReadAllBytes(filename);
var settings = new XmlWriterSettings
{
NewLineHandling = NewLineHandling.Entitize,
Indent = true,
IndentChars = indent,
NewLineChars = Environment.NewLine
};
Encoding utf8 = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);
using (var mem = new MemoryStream(input))
using (var sr = new StreamReader(mem, Encoding.UTF8))
using (var reader = XmlReader.Create(sr, new XmlReaderSettings { IgnoreWhitespace = true }))
using (var fs = File.Open(filename, FileMode.Create, FileAccess.Write, FileShare.None))
using (var sw = new StreamWriter(fs, utf8))
using (var writer = XmlWriter.Create(sw, settings))
{
writer.WriteNode(reader, false);
}
Также в идеале следует написать преамбулу
writer.WriteStartDocument();
Я не знаю подробностей об этом конкретном сериализаторе, но знаю, что сделать отступы в XML, который уже содержит пробелы, очень сложно. Проблема в том, что некоторые из существующих пробелов (например, в смешанном контенте) могут быть значительными. Вы говорите о «незначительных пробелах между тегами», но одна из больших проблем проектирования XML заключается в том, что различие между значимыми и незначащими пробелами никогда не бывает очень ясным.