用C#实现一个Json解析器(1)——功能清单

前言

Json是一种轻量级的数据交换格式,语法非常简单,同时能满足大部分需求。支持常见的几种数据类型,支持嵌套。因其体量轻、可以直接和前端JS交互的特点,常被用于前后端的数据传输。此外,由于轻便易解析,Json还在许多软件中代替XML、INI等格式作为配置文件使用。

下面我们用C#实现一个简单的Json解析器,在支持基本的对象和Json字符串映射功能的同时,附加一些有趣的小功能。

功能清单

我们先提出需求,把要实现的功能一一列出来,作为后面的设计和实现的参考。

基本功能

  1. 将Json字符串转换成C#对象。
  2. 将C#对象转换成Json字符串。

附加功能

  1. 用户可以指定Json字符串的键名和C#对象的属性名之间的映射。比如,可以让Json字符串的"Name"键和C#对象的"ID"属性对应。
  2. 用户可以指定哪些字段或属性被解析,哪些被忽略。
  3. 用户可以自定义某个Json字符串的键值对和C#对象的属性之间的转换函数。比如,可以让Json字符串的布尔型true转换成C#对象的字符串类型"是"。反之亦可。
  4. 用户可以自定义某个成员的约束条件。比如不能为空、字符串长度为10、数字在1到100之间等。

需求细化

  1. 对于需求一,我们考虑三种情况。①在编译期就能明确指出接收对象的静态类型的,使用泛型接口T ToObject<T>(String)返回对应类型的对象。②在运行时才能确定接收对象的静态类型的,使用接收Type对象的接口Object ToObject(Type, String)返回Object对象。③接收对象没有相应的静态类型的,使用接口dynamic ToObject(String)返回dynamic对象。
  2. 对于需求二,只有一种情况。因为这个方法对传入的对象没有任何约束,所以参数类型必须是Object,返回Json字符串。
  3. 需求三,为了让用户能自定义属性名和键名的对应关系,我们可以让接口额外接收一个字典对象,字典中包含了从属性名到键名的映射…开玩笑,太蠢了。为了让用户能自定义属性名和键名的对应关系,我们使用特性[Key(String)],这条特性可以标记字段或属性,它接收一个字符串参数,表示该成员对应的Json键名。
  4. 需求四,为了保证各种情况下的易用性,我们提供两对特性来配合使用。第一对是[ConvertAll]和[IgnoreAll],这两条特性可以标记类或结构体,它提示解析器解析或忽略所有成员;第二对是[Convert]和[Ignore],这条特性可以标记字段或属性,它提示解析器解析或忽略这个成员。默认情况下,公有属性会被解析,非公有属性和所有字段都会被忽略。优先级:Convert>Ignore>ConvertAll>IgnoreAll>默认。
  5. 需求五,Newtonsoft有相同的功能,他们采用的是基于接口继承的策略模式:他们向用户提供一个JsonConverter基类,其中定义了转换接口。当用户需要自定义转换函数时,便从此基类扩展出一个子类,实现转换接口,再通过一个[JsonConverter(Type)]特性来指向这个类的类对象。我们也采用策略模式,但不基于接口继承,而基于委托。定义两个委托Object ObjectConverter(String)和String JsonConverter(Object)分别表示从Json字符串到C#对象的转换函数和从C#对象到Json字符串的转换函数。我们允许用户向解析器中注册和注销这两种委托对象,每个委托对象唯一的64位整形ID用来查询。用户通过特性[ObjectConverter(Int64)]和[JsonConverter(Int64)]标注对应成员来提示解析器使用特定的转换函数。
  6. 需求六,我们提供了三个常用的用于表示约束条件的 特性:[NotNull]提示解析器此成员不能为空;[Range(double, double)]提示解析器此成员必须可以和实数比较且值域为[参数一, 参数二];[Length(int, int)]提示解析器此成员必须为字符串或数组且长度值域为[参数一, 参数二]。除了这些,还应该允许用户自定义约束条件,但是我想偷懒…大概可能也许在这个系列的最后如果我心血来潮了会加上的…

OK,到目前为止,本回的任务已经完成,我们已经列出了程序应该实现的所有功能并对每个功能进行了具体剖析。下回将进行建模,设计出程序的整体结构,并针对几个细节讨论几种实现方案。

发布了27 篇原创文章 · 获赞 41 · 访问量 2072

猜你喜欢

转载自blog.csdn.net/DIAX_/article/details/104273846