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

4

在XML文档中,除了0x09、0x0a、0x0d即制表符、回车符和换行符之外,任何低于0x20的字符都不能包含。

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

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

<rant>我觉得相当令人沮丧的是,这两个方面其实是同一枚硬币的两面,客户端和服务端都是.NET应用程序。如果没有任何东西可以读取,为什么soap格式化程序会写入不良的xml?</rant>

我想要:

  1. 使Xml Serialiser正确处理这些奇怪的字符;或者
  2. 让Web服务请求失败

我已经搜索了一些资料,但并没有找到太多信息,只有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时出现故障,但这仍然意味着我必须在发送数据之前对其进行清理。
由于这些字符是重要的,我不能只是剥离它们,我需要在传输之前对它们进行编码,读取时进行解码,我非常惊讶的是似乎没有现有的框架方法可以做到这一点。

你为什么要使用Soap Formatter?你具体在做什么? - John Saunders
@Inuyasha:我在尝试提取一个小样本时遇到了麻烦。Web服务传递的数据是在一个大型代码生成的模式库中。当我创建一个简单的Web服务和客户端时,它都可以运行。我现在正在逐层剥离WebService和Schema DLL,以找出故障点。感谢您的提示来帮助我隔离问题。 - Binary Worrier
1
根据您发布的代码,您没有使用SOAP格式化程序,而是使用XML序列化程序,这是完全不同的东西。我的问题是想问您为什么要使用SOAP格式化程序,因为它几乎不适用于任何工作。 - John Saunders
@John:在这种情况下,你会考虑哪种合适的工具?正如你所看到的,这不是我的专业领域。谢谢啦,伙计。 - Binary Worrier
通过使用服务引用调用 Web 服务。请参见如何消费 Web 服务以及其他许多地方。 - John Saunders
显示剩余2条评论
2个回答

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

首先:一个解决方法

我可以通过使用XmlWriter来写入Xml,这比客户端崩溃要好得多。例如:

然而,这并不能解决发送无效字符的根本问题。

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

    }
}

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接