Почему XmlWriter не всегда форматирует XML, как указано в XmlWriterSettings?

ФОН
Я получаю много 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 заключается в том, что различие между значимыми и незначащими пробелами никогда не бывает очень ясным.

Michael Kay 18.04.2024 11:55
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
1
52
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Проблема, с которой вы столкнулись, заключается в том, что вы удаляете исходный файл перед записью в него отформатированного 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 считывает содержимое исходного файла в строку задолго до его удаления, поэтому удаление исходного файла не представляет проблемы. Ваш код показывает точно такие же результаты.

René Vogt 18.04.2024 08:53

Удаление не требуется, поскольку File.WriteAllText() перезаписывает существующий файл. Я добавил его для удобства чтения, чтобы читателю не приходилось его искать.

GoWiser 18.04.2024 09:54
Ответ принят как подходящий

Проблема в том, что если читатель сохраняет незначительные пробелы, то для автора это теперь значительные пробелы.

Таким образом, он не может добавлять больше пробелов, так как это изменит смысл, или, по крайней мере, похоже, он не проверяет, что записываемый внутренний текст представляет собой только пробелы.

Таким образом, правильное решение — сначала удалить пробелы и переписать их, используя упомянутый вами код 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();

Другие вопросы по теме