惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Schneier on Security
A
Arctic Wolf
S
Security Affairs
O
OpenAI News
SecWiki News
SecWiki News
TaoSecurity Blog
TaoSecurity Blog
H
Heimdal Security Blog
T
Threat Research - Cisco Blogs
Hacker News: Ask HN
Hacker News: Ask HN
N
News | PayPal Newsroom
Google Online Security Blog
Google Online Security Blog
C
Cisco Blogs
The Hacker News
The Hacker News
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
C
CXSECURITY Database RSS Feed - CXSecurity.com
P
Privacy International News Feed
V
Vulnerabilities – Threatpost
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
酷 壳 – CoolShell
酷 壳 – CoolShell
H
Hacker News: Front Page
T
Tenable Blog
T
The Exploit Database - CXSecurity.com
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Spread Privacy
Spread Privacy
人人都是产品经理
人人都是产品经理
www.infosecurity-magazine.com
www.infosecurity-magazine.com
V2EX - 技术
V2EX - 技术
L
LINUX DO - 最新话题
The GitHub Blog
The GitHub Blog
博客园 - 三生石上(FineUI控件)
T
The Blog of Author Tim Ferriss
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
V
Visual Studio Blog
The Cloudflare Blog
N
News and Events Feed by Topic
量子位
Google DeepMind News
Google DeepMind News
Application and Cybersecurity Blog
Application and Cybersecurity Blog
L
LINUX DO - 热门话题
P
Palo Alto Networks Blog
Stack Overflow Blog
Stack Overflow Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Attack and Defense Labs
Attack and Defense Labs
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Hacker News - Newest:
Hacker News - Newest: "LLM"
Apple Machine Learning Research
Apple Machine Learning Research
The Register - Security
The Register - Security
Microsoft Security Blog
Microsoft Security Blog
Know Your Adversary
Know Your Adversary
Webroot Blog
Webroot Blog

博客园 - KevinWang

Server.HtmlEncode vs HttpUtility.HtmlEncode 在FireFox IE 下Response 中文文件名乱码问题 月薪上万的人都懂得这些道理 c# .ToString()格式化 利用ListView和DataPager控件来对数据分页 PageMethods Not Found (or Defined)? (转) asp.net中使用ajax中的三种方式 Asp.Net2.0技巧 - KevinWang - 博客园 虚方法(virtual)和抽象方法(abstract)的区别 数据库设计经验之谈(转载) 网站令浏览器崩溃的原因 各种流行的编程风格 2009年海外Web设计风潮 常用js框架,js库 硬盘RAID是什么意思?有什么用? T-SQL和PL/SQL 区别 What is PL/SQL?(PL/SQL是什么,与T-SQL对比) 数据库和数据仓库的区别 ASP.NET MVC 资料
序列化,反序列化时低序位非打印 ASCII 字符的问题 - KevinWang
KevinWang · 2011-02-19 · via 博客园 - KevinWang

原因:System.Xml.XmlException: “”(十六进制值 0x03)是无效的字符。 行 4,位置 24。 在 System.Xml.XmlTextReaderImpl.Throw(Exception e) 在 决方法

最近碰到一个问题,我的一个把数据库中记录的信息暴露出来的Web Service调用时候出问题了。报下面的错误信息:

System.InvalidOperationException was unhandled
  Message="XML 文档(1, 823)中有错误。"
  Source="System.Xml"
    Message="“”(十六进制值 0x0E)是无效的字符。 行 1,位置 823。"
    Source="System.Xml"

当这个错误发生时,Web Service 服务器端不会有任何错误,而调用这个 Web Service 的客户端则会报上述错误。
是何原因导致的这个问题呢? 
答案很简单,是WEB Service 暴露的XML文档中存在低序位非打印 ASCII 字符所致。
我们查看 Web Service 返回的XML 文档文档中,会有下面的XML文档节:其中的  就是低序位 ASCII 字符。对应的字符如后:

<Value>在神奇天地裏誰叱咤風雨</Value>

会导致这些问题的 低序位非打印 ASCII 字符包含以下字符:
#x0 - #x8 (ASCII 0 - 8)
#xB - #xC (ASCII 11 - 12)
#xE - #x1F (ASCII 14 - 31)

下面就是一个简单演示这个问题的控制台程序, 
为了简单起见,这里没有建立 WebService, 而是把一个类XML序列化存储到文件,然后再把这个文件反序列化读取出来:
其中的这个类的Value值中,放了一个低序位非打印 ASCII 字符。
执行这个控制台程序,就会报异常。“XML 文档(3, 12)中有错误。”

using System;
using System.Xml.Serialization;
using System.IO;
using System.Text;
using System.Globalization;

namespace TextSerialize
{
[Serializable]
public class MyClass
{
public string Value { get; set; }
}

class Program
{
static void Main(string[] args)
{
string fileName = "d:\\1.txt";

MyClass c = new MyClass();
c.Value = string.Format("在神奇{0}天地裏誰叱咤風雨", Convert.ToChar(14));

SaveAsXML(c, fileName, Encoding.UTF8);

object o = ConvertFileToObject(fileName, typeof(MyClass), Encoding.UTF8);
MyClass d = o as MyClass;
if (d != null) Console.WriteLine(d.Value);
else Console.WriteLine("null");

Console.ReadLine();
}

/// <summary>
/// 序列化
/// </summary>
/// <param name="objectToConvert"></param>
/// <param name="path"></param>
/// <param name="encoding"></param>
public static void SaveAsXML(object objectToConvert, string path, Encoding encoding)
{
if (objectToConvert != null)
{
Type t = objectToConvert.GetType();
XmlSerializer ser = new XmlSerializer(t);
using (StreamWriter writer = new StreamWriter(path, false, encoding))
{
ser.Serialize(writer, objectToConvert);
writer.Close();
}
}
}

/// <summary>
/// 反序列化
/// </summary>
/// <param name="path"></param>
/// <param name="objectType"></param>
/// <param name="encoding"></param>
/// <returns></returns>
public static object ConvertFileToObject(string path, Type objectType, Encoding encoding)
{
object convertedObject = null;
if (!string.IsNullOrEmpty(path))
{
XmlSerializer ser = new XmlSerializer(objectType);
using (StreamReader reader = new StreamReader(path, encoding))
{
convertedObject = ser.Deserialize(reader);
reader.Close();
}
}
return convertedObject;
}
}
}

上面提到的Web Service 的那个问题,跟这个演示程序是一样的。

我们需要被序列化的内容中,存在 低序位非打印 ASCII 字符 时, .net 会给我们正常序列化, 会自动把 低序位非打印 ASCII 字符 转换成 &#x 编码的字符(这个XML规范中要求这么做的)。

但是,反序列化时候,如果需要反序列化的内容如果存在 &#x 编码的字符(映射到低序位非打印 ASCII 字符),则反序列化就会出错。

如果解决这个问题呢?

当然,最彻底的解决方法是修改反序列化的代码,让这些字符不会出错。但这个东西很多时候不归我们控制。这个方案不可行。

下一个方案就是剔除这些捣乱的字符。

我这里要给出的方案,是对这些字符序列化时作一次预处理,反序列化时,作一次反向处理。
这里为了演示的更有意义,我这里处理逻辑就是把 低序位非打印 ASCII 字符 转换成 &#x 编码的字符 ,和把&#x 编码的字符 转换成 低序位非打印 ASCII 字符。 
这样就可以使用我这里提供的函数,实现更多的处理逻辑。这两个函数的代码如下:

        /// <summary>
/// 把一个字符串中的 低序位 ASCII 字符 替换成 &#x 字符
/// 转换 ASCII 0 - 8 -> � - 
/// 转换 ASCII 11 - 12 -> -
/// 转换 ASCII 14 - 31 ->  - 
/// </summary>
/// <param name="tmp"></param>
/// <returns></returns>
public static string ReplaceLowOrderASCIICharacters(string tmp)
{
StringBuilder info = new StringBuilder();
foreach (char cc in tmp)
{
int ss = (int)cc;
if (((ss >= 0) && (ss <= 8)) || ((ss >= 11) && (ss <= 12)) || ((ss >= 14) && (ss <= 32)))
info.AppendFormat("&#x{0:X};", ss);
else info.Append(cc);
}
return info.ToString();
}

/// <summary>
/// 把一个字符串中的下列字符替换成 低序位 ASCII 字符
/// 转换 � -  -> ASCII 0 - 8
/// 转换 - -> ASCII 11 - 12
/// 转换  -  -> ASCII 14 - 31
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static string GetLowOrderASCIICharacters(string input)
{
if (string.IsNullOrEmpty(input)) return string.Empty;
int pos, startIndex = 0, len = input.Length;
if (len <= 4) return input;

StringBuilder result = new StringBuilder();
while ((pos = input.IndexOf("&#x", startIndex)) >= 0)
{
bool needReplace = false;
string rOldV = string.Empty, rNewV = string.Empty;

int le = (len - pos < 6) ? len - pos : 6;
int p = input.IndexOf(";", pos, le);

if (p >= 0)
{
rOldV = input.Substring(pos, p - pos + 1);

// 计算 对应的低位字符
short ss;
if (short.TryParse(rOldV.Substring(3, p - pos - 3), NumberStyles.AllowHexSpecifier, null, out ss))
{
if (((ss >= 0) && (ss <= 8)) || ((ss >= 11) && (ss <= 12)) || ((ss >= 14) && (ss <= 32)))
{
needReplace = true;
rNewV = Convert.ToChar(ss).ToString();
}
}
pos = p + 1;
}
else pos += le;

string part = input.Substring(startIndex, pos - startIndex);
if (needReplace) result.Append(part.Replace(rOldV, rNewV));
else result.Append(part);

startIndex = pos;
}
result.Append(input.Substring(startIndex));
return result.ToString();
}

这样,我们这个演示程序的 Main 函数修改为下面的代码,也不会有任何错误发生。

        static void Main(string[] args)
{
Console.WriteLine(GetLowOrderASCIICharacters("123456񐀀"));
Console.WriteLine(GetLowOrderASCIICharacters("123456"));
Console.WriteLine(GetLowOrderASCIICharacters(""));
Console.WriteLine(GetLowOrderASCIICharacters("0123456789"));
Console.WriteLine(GetLowOrderASCIICharacters("\f"));
Console.WriteLine(GetLowOrderASCIICharacters("=-1"));
Console.WriteLine(GetLowOrderASCIICharacters(""));
Console.WriteLine(GetLowOrderASCIICharacters(""));

string fileName = "d:\\1.txt";

MyClass c = new MyClass();
c.Value = string.Format("在神奇{0}天地裏誰叱咤風雨", Convert.ToChar(14));
c.Value = ReplaceLowOrderASCIICharacters(c.Value);

SaveAsXML(c, fileName, Encoding.UTF8);

object o = ConvertFileToObject(fileName, typeof(MyClass), Encoding.UTF8);
MyClass d = o as MyClass;
if (d != null)
{
d.Value = GetLowOrderASCIICharacters(d.Value);
Console.WriteLine(d.Value);
}
else Console.WriteLine("null");

Console.ReadLine();
}

小结

低序位非打印 ASCII 字符 在很多时候会给我们的系统带来问题,这部分字符必须作特殊处理