XmlTextWriter неправильно пишет управляющие символы
.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  world</Test>
Ответы
Ответ 1
Эта документация о поведении скрыта в документации метода WriteString, но похоже, что она применима ко всему классу.
Поведение XmlWriter по умолчанию, созданное с помощью Create, - это бросить исключение ArgumentException при попытке записать значения символов в диапазон 0x-0x1F (исключая символы пробела 0x9, 0xA и 0xD). Эти недопустимые символы XML можно написать, создав XmlWriter с свойством CheckCharacters установлено значение false. Это приведет к при замене символов числовыми символьными сущностями (�
через �x1F
). Кроме того, созданный XmlTextWriter с новым оператор заменит недопустимые символы числовым символом по умолчанию.
Похоже, вы в конечном итоге пишете недопустимые символы, потому что используете класс XmlTextWriter. Лучшим решением для вас было бы использовать XmlWriter Class.
Ответ 2
Просто нашел этот вопрос, когда я боролся с одной и той же проблемой, и я решил решить его с помощью регулярного выражения:
return Regex.Replace(s, @"[\u0000-\u0008\u000B\u000C\u000E-\u001F]", "");
Надеюсь, что это поможет кому-то в качестве альтернативного решения.
Ответ 3
Встроенные .NET-escapers, такие как 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 должен был бы просто разделить их:
Googling немного дал белый список XmlTextEncoder, но он также удалит DEL
, а другие в диапазоне U + 007F-U +0084, U + 0086-U + 009F, которые согласно Valid XML Characters в википедии действительны только в определенных контекстах и которые RFC упоминает как обескураженные но все еще действительные символы.
public static class XmlTextExtentions
{
private static readonly Dictionary<char, string> textEntities = new Dictionary<char, string> {
{ '&', "&"}, { '<', "<" }, { '>', ">" },
{ '"', """ }, { '\'', "'" }
};
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
, Википедия и метки спецэффектов являются допустимым (хотя и обескураженным).