Как остановить.net Xml сериализацию вставки недопустимых символов
Все, что меньше 0x20 (за исключением 0x09, 0x0a, 0x0d, т. Е. Tab, возврат каретки и перевод строки), не может быть включено в документ XML.
У меня есть некоторые данные, поступающие из базы данных и передаваемые в ответ на запрос веб-службы.
Средство форматирования Soap успешно кодирует символ 0x12 (Ascii 18, Device Control 2) как 
но ответ не выполняется на клиенте с шестнадцатеричным значением 0x12, является недопустимым символом
<rant>
Что меня расстраивает, так это то, что это две стороны одной медали, и клиент, и сервис - это приложения.net. Почему средство форматирования мыла пишет плохой XML, если ничто не может его прочитать? </rant>
Я хотел бы либо
- Получить Xml Serialiser для правильной обработки этих нечетных символов или
- Ошибка запроса в веб-сервисе
Я погуглил и не смог найти ничего другого, кроме: а) "дезинфекции ваших входов" или б) "изменения структуры вашего документа".
а) не является бегуном, так как некоторым из этих данных +20 лет
б) это тоже не лучший вариант, так как кроме нашего собственного интерфейса, у нас есть клиенты, которые напрямую кодируют веб-службу.
Есть что-то очевидное, что я упускаю? Или это просто случай кода вокруг управляющих кодов AscII?
Спасибо
Обновить
Это на самом деле проблема с XmlSerialiser, следующий код будет сериализовать неверный символ в поток, но не десериализовать его
[Serializable]
public class MyData
{
public string Text { get; set; }
}
class Program
{
public static void Main(string[] args)
{
var myData = new MyData {Text = "hello "
+ ASCIIEncoding.ASCII.GetString(new byte[] { 0x12 })
+ " world"};
var serializer = new XmlSerializer(typeof(MyData));
var xmlWriter = new StringWriter();
serializer.Serialize(xmlWriter, myData);
var xmlReader = new StringReader(xmlWriter.ToString());
var newData = (MyData)serializer.Deserialize(xmlReader); // Exception
// hexadecimal value 0x12, is an invalid character.
}
}
Я могу заставить его задушить написание XML, явно создав XmlWriter и передав его Serialise
(Я опубликую это в ближайшее время как мой собственный ответ), но это все еще означает, что я должен санировать свои данные перед отправкой.
Поскольку эти символы имеют большое значение, я не могу просто удалить их, мне нужно кодировать их перед передачей и декодировать их при чтении, и я действительно очень удивлен, что, похоже, не существует существующего метода фреймворка для этого.
2 ответа
Второе: решение
С использованием DataContractSerializer
(который используется по умолчанию для служб WCF) вместо XmlSerializer
работает удовольствие
[Serializable]
public class MyData
{
public string Text { get; set; }
}
class Program
{
public static void Main(string[] args)
{
var myData = new MyData
{
Text = "hello "
+ ASCIIEncoding.ASCII.GetString(new byte[] { 0x12 })
+ " world"
};
var serializer = new DataContractSerializer(typeof(MyData));
var mem = new MemoryStream();
serializer.WriteObject(mem, myData);
mem.Seek(0, SeekOrigin.Begin);
MyData myData2 = (MyData)serializer.ReadObject(mem);
Console.WriteLine("myData2 {0}", myData2.Text);
}
}
Фрист: Обходной путь
Я могу заставить его задыхаться при написании Xml, используя XmlWriter, что, возможно, лучше, чем клиент, подавленный им. например
Однако это не решает основную проблему отправки недопустимых символов
[Serializable]
public class MyData
{
public string Text { get; set; }
}
class Program
{
public static void Main(string[] args)
{
var myData = new MyData {Text = "hello "
+ ASCIIEncoding.ASCII.GetString(new byte[] { 0x12 })
+ " world"};
var serializer = new System.Xml.Serialization.XmlSerializer(typeof(MyData));
var sw = new StringWriter();
XmlWriterSettings settings = new XmlWriterSettings();
using (var writer = XmlWriter.Create(sw))
{
serializer.Serialize(writer, myData); // Exception
// hexadecimal value 0x12, is an invalid character
}
var xmlReader = new StringReader(sw.ToString());
var newUser = (MyData)serializer.Deserialize(xmlReader);
Console.WriteLine("User Name = {0}", newUser);
}
}
Комбинация сообщения Binary Worrier со вставленным фильтром специальных символов работает очень хорошо, чтобы отфильтровать объект непосредственно перед его возвращением:
public List<MyData> MyWebServiceMethod()
{
var mydata = GetMyData();
return Helper.ScrubObjectOfSpecialCharacters<List<MyData>>(mydata);
}
Хелпер класс:
public static T ScrubObjectOfSpecialCharacters<T>(T obj)
{
var serializer = new XmlSerializer(obj.GetType());
using (StringWriter writer = new StringWriter())
{
serializer.Serialize(writer, obj);
string content = writer.ToString();
content = FixSpecialCharacters(content);
using (StringReader reader = new StringReader(content))
{
obj = (T)serializer.Deserialize(reader);
}
}
return obj;
}
public static string FixSpecialCharacters(string input)
{
if (string.IsNullOrEmpty(input)) return input;
StringBuilder output = new StringBuilder();
for (int i = 0; i < input.Length; i++)
{
int charCode = (int)input[i];
switch (charCode)
{
case 8211:
case 8212:
{
// replaces short and long hyphen
output.Append('-');
break;
}
default:
{
if ((31 < charCode && charCode < 127) || charCode == 9)
{
output.Append(input[i]);
}
break;
}
}
}
return output.ToString();
}