如何阻止.net Xml序列化插入非法字符

时间:2011-11-21 17:10:37

标签: .net xml soap invalid-characters

任何低于0x20的内容(0x09,0x0a,0x0d除外,即tab,carrige返回和换行)都不能包含在XML文档中。

我有一些来自数据库的数据,并作为对Web服务请求的响应传递。

Soap格式化程序很高兴地将0x12字符(Ascii 18,设备控制2)编码为,但客户端的响应失败,十六进制值为0x12,是无效字符

<rant>我发现相当令人沮丧的是这些是同一枚硬币的两面,客户端和服务都是.net应用程序。如果没有任何东西可以读取它,为什么soap格式化程序会写坏xml?</rant>

我想要

  1. 让Xml Serialiser正确处理这些奇怪的字符或
  2. 请求在Web服务中失败
  3. 我用谷歌搜索了除此之外找不到多少,a)“清理你的输入”或b)“改变你的文档结构”。

    a)不是跑步者,因为其中一些数据是+ 20岁 b)也不是一个选项,除了我们自己的前端,我们有客户端直接针对Web服务进行编码。

    有什么明显的东西我不见了吗?或者仅仅是围绕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.
    
        }
    }
    

    我可以通过显式创建一个XmlWriter并将其传递给Serialise来阻止编写 xml(我会在短期内将其作为我自己的答案发布),但这仍然意味着在发送之前我要对我的数据进行整理。
    由于这些字符非常重要,我不能剥离它们,我需要在传输之前对它们进行编码,并在读取时对其进行解码,而且我真的真的很惊讶,似乎没有现成的这样做的框架方法。

2 个答案:

答案 0 :(得分:1)

第二:解决方案

使用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来阻止它,这可能比客户端窒息更好。 e.g。

但是,它无法解决发送无效字符的潜在问题

[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);

    }
}

答案 1 :(得分:0)

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();
}