源码发布

搞了一个下午,终于搞定了这个号称中国的github...以后源码直接在这里发布了(github实在用不来,英文实在太烂了)

https://code.csdn.net/jy02305022/blqw-json

  相关回顾

  废话

自从上次发表了Json序列化的方案之后,已经整整一个月了。

原本是想序列化写完马上开始写反序列化的,但是来看了大家的回复之后得到了很多启示,所以这一个月直接在做优化的工作(当然还有带BB)。

我发现博客园真是个好地方,以前在QQ空间,点点,微博发表技术文章的时候根本没有人回复,了不起有几个转载的。。。

在这里大家一起参与讨论,才能获得更多的启示和发现,才能更好的提高自己!

  blqw.Json方案整体结构

blqw.Json

├─JsonBuilder                   //用于将C#转换为Json字符串

├─QuickJsonBuilder           //快速的将任意C#对象转换为Json字符串,继承自JsonBuilder

├─UnsafeStringWriter        //程序集可用,未公开对象.以非安全方式访问指针操作字符串直接写入内存,以提高字符串拼接效率

├─JsonParser                    //用于将Json字符串转换为C#对象

└─UnsafeJsonReader         //程序集可用,未公开对象.以非安全方式访问指针遍历内存中的字符串,以提高访问效率

ps:1,2,3是序列化用的,4,5是反序列化用的,项目还引用了Literacy用于IL方式反射访问对象属性 

  反序列化设计

反序列化相关的类只有2个UnsafeJsonReader,JsonParser 。一个用于读字符串,一个用于生成对象

UnsafeJsonReader 负责从Json字符串中读取指定的内容,读出的内容只能是最基本的String,Number,DateTime,true,false等

JsonParser 负责解析具体对象,并命令UnsafeJsonReader读取需要的内容,如果有必要,得到对象后进行一次转换然后赋值给对象

ps:JsonParser目前不支持转为DataSet或DataTable,因为把我觉得没什么必要,即使转了转过来都是String的也没什么用是吧

  • 图例

  • 示例代码
//json:{"Name":"blqw","Age":27}

class User
{
public string Name { get; set; }
public int Age { get; set; }
}
User ToUser(string json)
{
UnsafeJsonReader reader = new UnsafeJsonReader(json); //构造UnsafeJsonReader,这里只是例子而已,真是情况不是这样的 if (reader.SkipChar('{') == false) //跳过 左花括号 ,此操作忽略所有空格
{ //如果跳过空格后第一个字符不是{,返回false,如果是返回true,且跳过{
ThrowMissingCharException('{'); //返回false 则抛出异常 "缺少{符号"
} User user = new User();
while (true)
{
var pn = reader.ReadString(); //读取一个String ,String第一次字符必须是 双引号 或 单引号,否则会抛出异常
if (reader.SkipChar(':') == false) //跳过 冒号
{
ThrowMissingCharException(':'); //失败抛出异常
}
if (pn == "Name") //判断读出的String是Name还是Age 这只是个例子.....
{
user.Name = reader.ReadString();//如果是Name 继续出一个String,作为名称
}
else if (pn == "Age")
{
var num = reader.ReadConsts(); //如果是Age,则读出一个常量,可能是true,false,number,null,-Infinity,Infinity
//读取失败抛出异常
user.Age = (int)Convert.ChangeType(num, typeof(int));//转为int
}
if (reader.SkipChar(',')) //跳过一个 逗号
{
continue; //成功说明还有下一个属性
}
else if (reader.Current == '}') //如果失败,直接判断当前字符,如果是 右花括号,说明已经结束了
{
break;
}
else //既不是 逗号 也不是 右花括号 ,那就是作死的节奏了....
{
ThrowException("错误的结束符号:" + reader.Current);
}
}
reader.MoveNext(); //能到这里说明遇到右花括号了,跳过这个字符
if (reader.IsEnd()) //判断字符串是否已经结尾了,这个操作依然会跳过空白和回车字符
{
return user;
}
else //如果他还没有结束,我只能遗憾的说,你赢了!
{
ThrowException();
}
}

整体的流程大致就是这么一个情况,当然上面那个是精简的不能再精简的例子,真实情况要复杂很多,不过只要知道思路了对于我们程序猿来说,不是都差不多嘛

  异常设计

  • 如果User.Age是int类型的,但是Json字符串是这样的{age:"aa"}

    这点我参考了大多数人的做法,直接抛出。第一这样做对于性能的影响最小,第二这样做对于调用者来说最直观

    我之前的做法是忽略这个属性,后来发现这样做虽然程序没有异常了,但往往错误的时候也不知道,很多值都被直接以默认值的形式插入到数据库去了

  • 如果是Json属性不存在对象中 json:{"Name":"blqw", "Address":"gz"},Address不是类User的属性

    我使用一个叫SkipValue的方法,在字符串中立即跳过这个属性对应的值部分的字符串

  • 如果对象中的属性不在Json字符串中,就不管了,也不会有异常抛出

  性能设计

这里是比较重点要说明的,因为这部分是花时间最多的部分。

怎么才能设计出高效的反序列化方法?

  • 1.字符串尽量只遍历一次(UnsafeJsonReader就是用来干这个的),重复遍历毫无意义。当然有的时候为了保持程序的可读性不能不这样或者那样设计。这个时候就需要权衡可读性和性能的取舍了。
    例如:{"number":"123.165aafdsafdsafdsafds"},有些人会这样处理,先取出123.165aafdsafdsafdsafds,然后再判断是否是数字,这就是多此一举了,在遍历到第一个a的时候到直接就可以给出不是数字的结论了,为什么还要继续?
  • 2.所有对象只解析一次,在fastJson中,他将所有的字符串先解析成为一个List或者一个Dictionary,然后再吧Dictionary或者List解析成别的对象,这样做虽然可以使得程序可读性更好,便于维护。但是性能上的浪费是显而易见的。
  • 3.在字符串转换较慢的类型上重新实现转换方法,比如DateTime,Number(所有数字类型)---这里比较看个人水品了
  • 4.了解不同类型在性能上的细微差别,特别是可空值类型,我觉得他就是性能杀手(参考:学习笔记1,学习笔记2)
  • 5.使用最适合的方法处理,比如ReadString这个方法会从Json字符串当前位置读取一个String并返回,有的时候我只想跳过,不想返回,当然使用ReadString也是可以达到呀求的,只要不处理返回值就行了。但是这在性能上就有所浪费了,既然不用,那就不要返回,重新实现一个SkipString会比较合适
/// <summary> 读取字符串
/// </summary>
public string ReadString()
{
if (IsEnd())//已到结尾
{
ThrowException();
} Char quot = _Current;
if (quot != '"' && quot != '\'')
{
ThrowException();
}
MoveNext();
if (_Current == quot)
{
MoveNext();
return "";
} var index = _Position;
MiniBuffer buff = null; do
{
if (_Current == '\\')//是否是转义符
{
if ((WordChars[_Current] & ) == )
{
ThrowException();
}
if (buff == null)
{
//锁定指针
char* p = stackalloc char[];
buff = new MiniBuffer(p);
}
buff.AddString(_P, index, _Position - index);
MoveNext();
}
MoveNext();
} while (_Current != quot);//是否是结束字符
string str;
if (buff == null)
{
str = new string(_P, index, _Position - index);
}
else
{
buff.AddString(_P, index, _Position - index);
str = buff.ToString();
}
MoveNext();
return str;
}

ReadString

/// <summary> 跳过一个字符串
/// </summary>
public void SkipString()
{
if (IsEnd())//已到结尾
{
ThrowException();
}
Char quot = _Current;
if (quot != '"' && quot != '\'')
{
ThrowException();
}
MoveNext();
while (_Current != quot)//是否是结束字符
{
MoveNext();
if (_Current == '\\')//是否是转义符
{
if ((WordChars[_Current] & ) == )
{
ThrowException();
}
MoveNext();
}
}
MoveNext();
}

SkipString

可以看到这2个方法的代码量都不是一个级别的,性能自然不用说

  • 6.使用指针.在类中使用指针一定要注意锁定指针,不然很容易读取到错误的内存块
  • 7.善于使用性能分析工具帮助你找出你程序中占用时间长的函数,并合理的修改他
  • 8.防止过度设计!(这点我感觉非常重要,最早版本的反序列化一共设计了4个类,就属于过度设计了,不仅增加了代码的复杂度而且降低了性能,都后都精简了)
  • 9.合理利用一些小技巧

比如,如何判断一个char是数字还是字母?

常规的方法是

char c = '\0';
if (c >= '' && c <= '')
{
// c是数字
}
else if ((c >= 'a' && c <= 'z') || c >= 'A' && c <= 'Z')
{
//c是字母
}
else if (c == '\'' && c == '"')
{
//c是单引号或双引号
}
else if (c == ':')
{
//c是冒号
}
else//if ....
{
//....
}

修改后的方法

/// <summary>
/// <para>1: 数字</para>
/// <para>2: 字母</para>
/// <para>3: 双引号或单引号</para>
/// <para>4: 冒号</para>
/// <para>...</para>
/// <para></para>
/// </summary>
readonly static byte[] Chars = new byte[char.MaxValue]; static Program()
{
for (char c = ''; c <= ''; c++)
{
Chars[c] = ;
}
for (char c = 'a'; c <= 'z'; c++)
{
Chars[c] = ;
}
for (char c = 'A'; c <= 'Z'; c++)
{
Chars[c] = ;
}
Chars['\''] = ;
Chars['"'] = ;
Chars[':'] = ;
//...
}
char c = '\0';
switch (Chars[c])
{
case :
// c是数字
break;
case :
//c是字母
break;
case :
//c是单引号或双引号
break;
case :
//c是冒号
break;
default:
//...
break;
}

  效果

  • 序列化

  • 反序列化

  性能

这里提供一份几个常用Json组件的性能测试,也可以看出优化后的性能变化

  • 测试对象:

   源码下载

包含测试代码,这个以后就不更新了,要更新的去下面的code.csdn下载

http://files.cnblogs.com/blqw/blqw.Json.rar

  源码发布

搞了一个下午,终于搞定了这个号称中国的github...以后源码直接在这里发布了(github实在用不来,英文实在太烂了)

https://code.csdn.net/jy02305022/blqw-json

各位看官博友,看完之后如果对你有所启发请别忘了点一下推荐,让其他人也可以看到

如果有不同意见欢迎留言一起讨论

迟来的Json反序列化的更多相关文章

  1. Json反序列化

    迟来的Json反序列化   源码发布 搞了一个下午,终于搞定改了这个号称中国的github...以后源码直接在这里发布了(英文实在太烂了) https://code.csdn.net/jy023050 ...

  2. C# Json反序列化处理

    最近换工作了 从客户端转到Web端 第一个任务就是去别人的页面上抓取数据 用到的是JSON 因为他们网站json的格式有点怪 所以 就在JSON反序列化上面 花了一点时间 首先用到的工具是http:/ ...

  3. C#在Json反序列化中处理键的特殊字符

    假设有如下Json 数据: 1.{ 2."id" : 1, 3."@value" : "this a @", 4."$p" ...

  4. C# Json反序列化

    Json反序列化有两种方式[本人],一种是生成实体的,方便处理大量数据,复杂度稍高,一种是用匿名类写,方便读取数据,较为简单. 使用了Newtonsoft.Json,可以自行在nuget中导入 Jso ...

  5. .net Json 反序列化时,属性带点

    .net Json 反序列化时,属性带点 使用[JsonProperty("xxx.xxx")] static void Main(string[] args) { string ...

  6. Newtonsoft.Json反序列化(Deserialize)出错:Bad JSON escape sequence

    使用Newtonsoft.Json反序列化收到的字串为JObject或其它支持的数据模型,有时错误,提示如下: Bad JSON escape sequence: \c. Path , positio ...

  7. C# json反序列化 对象中嵌套数组 (转载) 可能会导致循环或多重级联路径。请指定 ON DELETE NO ACTION 或 ON UPDATE NO ACTION,或修改其他 FOREIGN KEY 约束。

    C# json反序列化 对象中嵌套数组 (转载)   看图: 这里可以看到是二层嵌套!!使用C#如何实现?? 思路:使用list集合实现 → 建立类 → list集合 → 微软的   Newtonso ...

  8. C# Json反序列化 C# 实现表单的自动化测试<通过程序控制一个网页> 验证码处理类:UnCodebase.cs + BauDuAi 读取验证码的值(并非好的解决方案) 大话设计模式:原型模式 C# 深浅复制 MemberwiseClone

    C# Json反序列化   Json反序列化有两种方式[本人],一种是生成实体的,方便处理大量数据,复杂度稍高,一种是用匿名类写,方便读取数据,较为简单. 使用了Newtonsoft.Json,可以自 ...

  9. json-lib json反序列化——日期转换

    将json格式的字符串转为对象,其中key-value有将String的日期转为Date类型,怪现象就是,转出来的Date类型的值是当前的系统时间. 网上有许多答案,在反序列化之前需要注册Date解析 ...

随机推荐

  1. SQLSERVER走起微信公众帐号全新改版 全新首页

    SQLSERVER走起微信公众帐号全新改版 全新首页 今天,SQLSERVER走起微信公众帐号增加了首页功能 虽然还是订阅号,不过已经对版面做了比较大的修改,希望各位亲用得放心.用得安心O(∩_∩)O ...

  2. LeetCode: 3Sum

    Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find all un ...

  3. windows环境下sublime的nodejs插件详细安装图解

    前面的话   搜索了好多文档后,才成功地安装了sublime text3的nodejs插件.为了存档,也为了方便有同样需求的朋友,将其安装过程详细记录如下 安装nodejs 虽然nodejs官网提供了 ...

  4. 《JavaScript设计模式与开发实践》整理

    最近在研读一本书<JavaScript设计模式与开发实践>,进阶用的. 一.高阶函数 高阶函数是指至少满足下列条件之一的函数. 1. 函数可以作为参数被传递. 2. 函数可以作为返回值输出 ...

  5. 邮件中嵌入html中要注意的样式

    工作中常会有需求向用户发送邮件,需要前端工程师来制作html格式的邮件,但是由于邮件客户端对样式的支持有限,要兼容很多种浏览器需要注意很多原则: 1.邮件使用table+css布局 2.邮件主要部分在 ...

  6. springMVC学习笔记--知识点总结1

    以下是学习springmvc框架时的笔记整理: 结果跳转方式 1.设置ModelAndView,根据view的名称,和视图渲染器跳转到指定的页面. 比如jsp的视图渲染器是如下配置的: <!-- ...

  7. HTML简单入门内容

    常用属性: Width=宽度 Height=高度 Size=大小 Color=颜色 Align=布局方向,值包括(top,bottom,left,right,center)上,下,左,右,中. Bor ...

  8. 页面布局class常见命名规范

    头:header 内容:content/container 尾:footer 导航:nav 侧栏:sidebar 栏目:column 页面外围控制整体布局宽度:wrapper 左右中:left rig ...

  9. MongoDB基础

    1.概念及特点 说明:由于部分语句中$ 符号无法正常显示,使用¥代表 概念 MongoDB是一个基于文档的分布式的开源的NoSQL数据库,文档的结构为BSON形式,每一个文档都有一个唯一的Object ...

  10. .NET面试题系列[3] - C# 基础知识(1)

    1 类型基础 面试出现频率:基本上肯定出现 重要程度:10/10,身家性命般重要.通常这也是各种招聘工作的第一个要求,即“熟悉C#”的一部分.连这部分都不清楚的人,可以说根本不知道自己每天都在干什么. ...