XmlTextWriter неправильно пишет управляющие символы - PullRequest
13 голосов
/ 24 ноября 2011

.NET XmlTextWriter создает недопустимые файлы XML.

В XML разрешены некоторые управляющие символы, такие как «горизонтальная табуляция» (	), а другие - «вертикальная табуляция» (). (См. spec .)

У меня есть строка, которая содержит управляющий символ UTF-8, который не разрешен в XML.
Хотя XmlTextWriter экранирует символ, полученный XML-код, конечно, по-прежнему недействителен.

Как я могу убедиться, что XmlTextWriter никогда не создает недопустимый XML-файл?

Или, если это невозможно сделать с помощью XmlTextWriter, как я могу отделить определенные управляющие символы, которые не разрешены в XML, от строки?

Пример кода:

using (XmlTextWriter writer =
  new XmlTextWriter("test.xml", Encoding.UTF8))
{
  writer.WriteStartDocument();
  writer.WriteStartElement("Test");
  writer.WriteValue("hello \xb world");
  writer.WriteEndElement();
  writer.WriteEndDocument();
}

Выход:

<?xml version="1.0" encoding="utf-8"?><Test>hello &#xB; world</Test>

Ответы [ 3 ]

12 голосов
/ 24 ноября 2011

Эта документация поведения скрыта в документации метода WriteString , но похоже, что она применяется ко всему классу.

Поведение по умолчанию для созданного XmlWriterс помощью Create создается исключение ArgumentException при попытке записать символьные значения в диапазоне 0x-0x1F (исключая пробельные символы 0x9, 0xA и 0xD).Эти недопустимые символы XML можно записать, создав XmlWriter со свойством CheckCharacters, установленным в false.В результате этого символы будут заменены числовыми символами (от &#0; до &#0x1F).Кроме того, XmlTextWriter, созданный с помощью оператора new, по умолчанию заменяет недопустимые символы числовыми символьными объектами.

Таким образом, кажется, что в итоге вы пишете недопустимые символы, поскольку используете класс XmlTextWriter.Лучшее решение для вас - использовать XmlWriter Class .

3 голосов
/ 27 июня 2013

Только что нашел этот вопрос, когда я боролся с той же проблемой, и в итоге решил ее с помощью регулярного выражения:

return Regex.Replace(s, @"[\u0000-\u0008\u000B\u000C\u000E-\u001F]", "");

Надеюсь, это поможет кому-то в качестве альтернативного решения.

1 голос
/ 25 ноября 2011

Встроенные в .NET эскаперы, такие как SecurityElement.Escape, также не экранируют и не удаляют его.

  • Вы можете установить CheckCharacters на false как на устройстве записи, так и на устройстве чтения, если только ваше приложение взаимодействует с файлом. Полученный файл XML все равно будет технически недействительным .

См:

XmlWriterSettings xmlWriterSettings = new XmlWriterSettings();
xmlWriterSettings.Encoding = new UTF8Encoding(false);
xmlWriterSettings.CheckCharacters = false;
var sb = new StringBuilder();
var w = XmlWriter.Create(sb, xmlWriterSettings);
w.WriteStartDocument();
w.WriteStartElement("Test");
w.WriteString("hello \xb world");
w.WriteEndElement();
w.WriteEndDocument();
w.Close();
var xml = sb.ToString();
  • Если установка CheckCharacters на true (что по умолчанию) слишком жесткая, поскольку она просто вызовет исключение, альтернативный подход, который более мягок по отношению к недопустимым символам XML, заключался бы в их удалении:

Погуглив немного, вы получили белый список XmlTextEncoder , однако он также удалит DEL и другие в диапазоне U + 007F – U + 0084, U + 0086 – U + 009F, что в соответствии с Допустимые символы XML в Википедии действительны только в определенных контекстах, которые RFC упоминает как обескураживающие, но все еще действительные символы.

public static class XmlTextExtentions
{
    private static readonly Dictionary<char, string> textEntities = new Dictionary<char, string> {
        { '&', "&amp;"}, { '<', "&lt;" }, { '>', "&gt;" }, 
        { '"', "&quot;" }, { '\'', "&apos;" }
    };
    public static string ToValidXmlString(this string str)
    {
        var stripped = str
            .Select((c,i) => new 
            { 
                c1 = c, 
                c2 = i + 1 < str.Length ? str[i+1]: default(char),
                v = XmlConvert.IsXmlChar(c),
                p = i + 1 < str.Length ? XmlConvert.IsXmlSurrogatePair(str[i + 1], c) : false,
                pp = i > 0 ? XmlConvert.IsXmlSurrogatePair(c, str[i - 1]) : false
            })
            .Aggregate("", (s, c) => {                  
                if (c.pp)
                    return s;
                if (textEntities.ContainsKey(c.c1))
                    s += textEntities[c.c1];
                else if (c.v)
                    s += c.c1.ToString();
                else if (c.p)
                    s += c.c1.ToString() + c.c2.ToString();
                return s;
            });
        return stripped;
    }
}

Это проходит все тесты XmlTextEncoder, за исключением того, который ожидает от него удаления DEL, который XmlConvert.IsXmlChar, Википедия и спецификация помечают как действительный (хотя и не рекомендуется) символ.

...