c# 如何实现一个简单的json解析器

作者:NewAI 时间:2021-06-10 15:02:54 

一、JSON格式介绍

  • JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。相对于另一种数据交换格式 XML,JSON 有着很多优点。例如易读性更好,占用空间更少等。在 web 应用开发领域内,得益于 JavaScript 对 JSON 提供的良好支持,JSON 要比 XML 更受开发人员青睐。所以作为开发人员,如果有兴趣的话,还是应该深入了解一下 JSON 相关的知识。本着探究 JSON 原理的目的,我将会在这篇文章中详细向大家介绍一个简单的JSON解析器的解析流程和实现细节。由于 JSON 本身比较简单,解析起来也并不复杂。所以如果大家感兴趣的话,在看完本文后,不妨自己动手实现一个 JSON 解析器。好了,其他的话就不多说了,接下来让我们移步到重点章节吧。

  • 在线JOSN校验格式化工具 如果在解析字符串的时候,拿不准这个是不是正确的JOSN,你可以在这个上面测试一下,有利于对自己代码的测试

二、解析原理介绍

  • 解析对象{}

对象结构是{"Key":[值]}的格式,所以先解析到Key字符串,将Key解析出来,然后在解析到值,因为值有可能是【字符串、值类型、布尔类型、对象、数组、null】所以需要根据前缀得到类型,并调用相应的解析方法,循环解析到“}”对象结尾

  • 解析数组[]

对象的结构是[[值],[值]],因为值有可能是【字符串、值类型、布尔类型、对象、数组、null】所以需要根据前缀得到类型,并调用相应的解析方法,循环解析到]数组结尾

  • 解析字符串

循环解析,需要判断是否遇到转义符\如果遇到,当前字符的下一个字符将是作为普通字符存入结果,如果遇到非转义的 " 字符则退出字符串读取方法,并返回结果

  • 解析值类型

循环拉取[0-9]包括.符号,然后调用转换成double类型方法

  • 解析布尔类型

转判断是 true 还是 false

  • 解析null

转判断是否为 null

解析元素流程图

c# 如何实现一个简单的json解析器

解析方法列表

方法名方法作用
AnalysisJson解析JSON字符串为C#数据结构
AnalysisJsonObject解析JSON字符串为对象结构
AnalysisJsonArray解析JSON字符串为数组结构
ReadElement读取出一个JSON结构
ReadJsonNumber读取出一个值类型结构
ReadJsonNull读取出一个null结构
ReadJsonFalse读取出一个false结构
ReadJsonTrue读取出一个true结构
ReadString读取出一个字符串结构
ReadToNonBlankIndex读取到非空白字符下标位置

例1 解析JSON

{"Name":"张三","Age":18}

1.解析第一个字符{发现是JSON对象结构,调用AnalysisJsonObject方法来解析JSON对象格式

2.解析对象的方法开始循环解析 Key-Value结构直到}对象尾部字符

  • 先解析Key结构调用 ReadString来进行解析出Key字符串从而得到Name这个值

  • 然后解析Value因为值可能是任意结构所以调用ReadElement来解析出一个JSON结构

  • 读取第一个字符得到"从而知道这个Value是一个字符串,调用方法ReadString来读取到这个Value的值张三

  • 读取下一个字符发现不是JSON对象的结尾字符}是,字符代表下面还存在一个Key-Value结构,继续读取

  • 先解析Key结构调用 ReadString来进行解析出Key字符串从而得到Age这个值

  • 然后解析Value因为值可能是任意结构所以调用ReadElement来解析出一个JSON结构

  • 读取第一个字符发现是1是数字,代表下面的这个结构是数值类型调用方法ReadJsonNumber来读取数值类型

  • 读取下一个字符发现是}是JSON对象的结尾字符,退出JSON对象解析,返回解析的JSON对象结构实例

例2 解析JSON

[{"科目":"语文","成绩":99}]

1.解析第一个字符[发现是JSON数组结构,调用方法AnalysisJsonArray方法来解析出JSON数组结构

解析循环解析JSON数据结构直到遇到]数组结构结尾字符

因为数组中每个元素都是可能是任意类型数据,所以调用ReadElement方法来解析值

读取值的第一个字符{发现是JSON对象类型调用AnalysisJsonObject方法解析JSON对象

先解析Key结构调用 ReadString来进行解析出Key字符串从而得到科目这个值

然后解析Value因为值可能是任意结构所以调用ReadElement来解析出一个JSON结构

读取第一个字符得到"从而知道这个Value是一个字符串,调用方法ReadString来读取到这个Value的值语文

读取下一个字符发现不是JSON对象的结尾字符}是,字符代表下面还存在一个Key-Value结构,继续读取

先解析Key结构调用 ReadString来进行解析出Key字符串从而得到成绩这个值

然后解析Value因为值可能是任意结构所以调用ReadElement来解析出一个JSON结构

读取第一个字符发现是9是数字,代表下面的这个结构是数值类型调用方法ReadJsonNumber来读取数值类型

读取下一个字符发现是}是JSON对象的结尾字符,退出JSON对象解析,返回解析的JSON对象结构实例

读取下一个字符发现是]JSON数组的结尾,退出解析JSON数组,返回解析的JSON数组结构实例

三、代码实现


 /// <summary>
 /// JSON解析类型
 /// </summary>
 public static class JsonConvert
 {
   /// <summary>
   /// 解析JSON
   /// </summary>
   /// <param name="text">待解析的JSON字符串</param>
   /// <returns>解析完成的JSON结构对象</returns>
   public static JsonElement AnalysisJson(string text)
   {
     var index = 0;
     //读取到非空白字符
     ReadToNonBlankIndex(text, ref index);
     if (text[index++] == '[')
       //解析数组
       return AnalysisJsonArray(text, ref index);
     //解析对象
     return AnalysisJsonObject(text, ref index);
   }

/// <summary>
   /// 解析JSON对象
   /// </summary>
   /// <param name="text">JSON字符串</param>
   /// <param name="index">开始索引位置</param>
   /// <returns>JSON对象</returns>
   private static JsonObject AnalysisJsonObject(string text, ref int index)
   {
     var jsonArray = new JsonObject();
     do
     {
       ReadToNonBlankIndex(text, ref index);
       if (text[index] != '"') throw new JsonAnalysisException($"不能识别的字符“{text[index]}”!应为“\"”",index);
       index++;
       //读取字符串
       var name = ReadString(text, ref index);
       ReadToNonBlankIndex(text, ref index);
       if (text[index] != ':') throw new JsonAnalysisException($"不能识别的字符“{text[index]}”!",index);
       index++;
       ReadToNonBlankIndex(text, ref index);
       if (jsonArray.ContainsKey(name)) throw new JsonAnalysisException($"已经添加键值:“{name}”",index);
       //读取下一个Element
       jsonArray.Add(name, ReadElement(text, ref index));
       //读取到非空白字符
       ReadToNonBlankIndex(text, ref index);
     } while (text[index++] != '}');

return jsonArray;
   }

/// <summary>
   /// 解析JSON数组
   /// </summary>
   /// <param name="text">JSON字符串</param>
   /// <param name="index">开始索引位置</param>
   /// <returns>JSON数组</returns>
   private static JsonArray AnalysisJsonArray(string text, ref int index)
   {
     var jsonArray = new JsonArray();
     do
     {
       ReadToNonBlankIndex(text, ref index);
       //读取下一个Element
       jsonArray.Add(ReadElement(text, ref index));
       //读取到非空白字符
       ReadToNonBlankIndex(text, ref index);
     } while (text[index++] != ']');

return jsonArray;
   }

/// <summary>
   /// 读取JSONElement
   /// </summary>
   /// <param name="text">字符串</param>
   /// <param name="index">开始下标</param>
   /// <returns>下一个Element</returns>
   private static JsonElement ReadElement(string text, ref int index)
   {
     switch (text[index++])
     {
       case '[':
         return AnalysisJsonArray(text, ref index);
       case '{':
         return AnalysisJsonObject(text, ref index);
       case '"':
         return new JsonString(ReadString(text, ref index));
       case 't':
         return ReadJsonTrue(text, ref index);
       case 'f':
         return ReadJsonFalse(text, ref index);
       case 'n':
         return ReadJsonNull(text, ref index);
       case '0':
       case '1':
       case '2':
       case '3':
       case '4':
       case '5':
       case '6':
       case '7':
       case '8':
       case '9':
         return ReadJsonNumber(text, ref index);
       default:
         throw new JsonAnalysisException($"未知Element“{text[index]}”应该为【[、{{、\"、true、false、null】", index);
     }
   }

/// <summary>
   /// 读取值类型
   /// </summary>
   /// <param name="text">JSON字符串</param>
   /// <param name="index">开始索引</param>
   /// <returns>JSON数值类型</returns>
   private static JsonNumber ReadJsonNumber(string text, ref int index)
   {
     var i = index;
     while (i < text.Length && char.IsNumber(text[i]) || text[i] == '.') i++;
     if (double.TryParse(text.Substring(index - 1, i - index + 1), out var value))
     {
       index = i;
       return new JsonNumber(value);
     }

throw new JsonAnalysisException("不能识别的数字类型!", i);
   }

/// <summary>
   /// 读取NULL
   /// </summary>
   /// <param name="text">JSON字符串</param>
   /// <param name="index">开始索引</param>
   /// <returns>读取NULL</returns>
   private static JsonNull ReadJsonNull(string text, ref int index)
   {
     if (text[index++] == 'u' &&
       text[index++] == 'l' &&
       text[index++] == 'l')
     {
       return new JsonNull();
     }

throw new JsonAnalysisException("读取null出错!", index);
   }

/// <summary>
   /// 读取FALSE
   /// </summary>
   /// <param name="text">JSON字符串</param>
   /// <param name="index">开始索引</param>
   /// <returns>布尔值-假</returns>
   private static JsonBoolean ReadJsonFalse(string text, ref int index)
   {
     if (text[index++] == 'a' &&
       text[index++] == 'l' &&
       text[index++] == 's' &&
       text[index++] == 'e')
     {
       return new JsonBoolean(false);
     }

throw new JsonAnalysisException("读取布尔值出错!", index);
   }

/// <summary>
   /// 读取TRUE
   /// </summary>
   /// <param name="text">JSON字符串</param>
   /// <param name="index">开始索引</param>
   /// <returns>布尔值-真</returns>
   private static JsonBoolean ReadJsonTrue(string text, ref int index)
   {
     if (text[index++] == 'r' &&
       text[index++] == 'u' &&
       text[index++] == 'e')
     {
       return new JsonBoolean(true);
     }
     throw new JsonAnalysisException("读取布尔值出错!",index);
   }

/// <summary>
   /// 读取字符串
   /// </summary>
   /// <param name="text">JSON字符串</param>
   /// <param name="index">开始索引</param>
   /// <returns>字符串值</returns>
   private static string ReadString(string text, ref int index)
   {
     //是否处于转义状态
     var value = new StringBuilder();
     while (index < text.Length)
     {
       var c = text[index++];
       if (c == '\\')
       {
         value.Append('\\');
         if (index >= text.Length)
           throw new JsonAnalysisException("未知的结尾!",index);
         c = text[index++];
         value.Append(c);
         if (c == 'u')
         {
           for (int i = 0; i < 4; i++)
           {
             c = text[index++];
             if (IsHex(c))
             {
               value.Append(c);
             }
             else
             {
               throw new JsonAnalysisException("不是有效的Unicode字符!",index);
             }
           }
         }
       }
       else if (c == '"')
       {
         break;
       }
       else if (c == '\r' || c == '\n')
       {
         throw new JsonAnalysisException("传入的JSON字符串内容中不允许有换行!",index);
       }
       else
       {
         value.Append(c);
       }
     }

return value.ToString();
   }

/// <summary>
   /// 判断是否为16进制字符
   /// </summary>
   private static bool IsHex(char c)
   {
     return c >= '0' && c <= '9' || c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z';
   }

/// <summary>
   /// 读取到非空白字符
   /// </summary>
   /// <param name="text">字符串</param>
   /// <param name="index">开始下标</param>
   /// <returns>非空白字符下标</returns>
   private static void ReadToNonBlankIndex(string text, ref int index)
   {
     while (index < text.Length && char.IsWhiteSpace(text[index])) index++;
   }
 }

完整DEMO代码下载

Github项目地址(会持续更新):DEMO代码

来源:https://www.cnblogs.com/liuzhenliang/archive/2020/07/20/AnalysisJson.html

标签:c#,实现,json,解析器
0
投稿

猜你喜欢

  • Java判断所给年份是平年还是闰年

    2023-10-21 17:48:59
  • java使用dom4j操作xml示例代码

    2022-03-21 18:28:38
  • 使用JDBC实现数据访问对象层(DAO)代码示例

    2021-11-12 23:33:46
  • Spring Boot示例代码整合Redis详解

    2022-03-20 19:23:37
  • SpringBoot中@Import注解如何正确使用

    2023-07-28 12:36:16
  • Struts2拦截器 关于解决登录的问题

    2023-07-02 14:06:09
  • SpringMVC框架实现图片上传与下载

    2022-01-12 23:50:52
  • android viewpaper实例探讨

    2023-03-21 12:47:55
  • Java程序图形用户界面设计之按钮与布局

    2023-07-18 07:03:21
  • SpringBoot 嵌入式web容器的启动原理详解

    2021-12-29 23:23:14
  • 详解大数据处理引擎Flink内存管理

    2023-03-09 14:41:43
  • 关于C#连接FTP时路径问题的解决方法

    2021-10-03 18:53:50
  • SpringMVC bean加载控制的实现分析

    2023-08-08 18:46:19
  • Java 全方位讲解面向对象特点与使用

    2023-07-21 18:34:06
  • java向下转型基础知识点及实例

    2022-07-01 11:48:38
  • Java如何实现登录token令牌

    2022-05-19 16:58:11
  • Java SpringBoot项目如何优雅的实现操作日志记录

    2022-01-28 11:37:41
  • Android网格布局GridView学习使用

    2023-02-07 15:03:13
  • C#获取上个月第一天和最后一天日期的方法

    2023-02-22 07:33:44
  • Mybatis中where标签与if标签结合使用详细说明

    2021-07-27 08:15:53
  • asp之家 软件编程 m.aspxhome.com