C++的Json库的简单实现

2023-11-13

Json

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。 易于人阅读和编写。同时也易于机器解析和生成。 它基于JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999的一个子集。 JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C, C++, C#, Java, JavaScript, Perl, Python等)。 这些特性使JSON成为理想的数据交换语言。

JSON建构于两种结构:

  • “名称/值”对的集合(A collection of name/value pairs)。不同的语言中,它被理解为对象(object),纪录(record),结构(struct),字典(dictionary),哈希表(hash table),有键列表(keyed list),或者关联数组 (associative array)。
  • 值的有序列表(An ordered list of values)。在大部分语言中,它被理解为数组(array)。

以上是我在JSON的网站上抄的

实现Json

Json有很多版本,许多比较牛的公司都实现过Json库,但是对于我来说还是个学习的过程,因为写这个项目并不是因为是简单的用自己的方式实现Json,而是通过学习Json库的同时学习一些别的东西,比如写测试单元,调试等,因此最开始从GitHub上找到Milo Yip大神的Json教程,根据教程将Json库用C语言实现了一下,简单的将其实现之后用C/C++将所有代码重写了一下,本文将讲一下我用C++实现时候的思路,C版本的Json是自己之前跟着教程练习的时候写的,就不细细讲了。

我的源码(点这里)和效果图

下面贴一下我的Json实现效果
Json Parse

Json库中的类实现

因为重新用C/C++写的时候希望将接口同已经有的库接口尽量统一,因此写的时候参考的是Jsoncpp的接口,并且类的结构也是参照Jsoncpp实现了Reader、Writer、Value类。

JSON::Value类

Value类是一个提供Json数据存储的一个类,实现增删查改等都是在这个类里面实现,他可以是空(null)、布尔值(true、false)、整型、无符号整型、浮点型、字符串、数组、对象其中的任意一个类型,因此这个类里面我设计成了一个包括所有类型的类,用了一个type标记了Value对象是表示什么类型,其中数组则是使用vector,对象则用map存储。
Value不止承担存储的任务,还承担了增删查改的任务,以及一些其他的任务,因此需要提供一些相关的接口,我实现了append()这种增加对象的接口,removeMember()删除对象的接口,一些类似于map中通过[]来获得和修改Value值的接口,也实现了=修改double、string的接口,以及提供了size()resize()clear()asBool()asString()getMemberNames()等一些功能性接口。
这里可以参考一下Jsoncpp中的接口,贴一下别人博客的链接就不粘贴复制了~~
Jsoncpp的源码地址

JSON::Reader

Reader类相当于教程中的解析类,提供所有类型的解析方法,我这里没有实现intuint的解析,所以数字只能用double存(不骗你我只是懒,其实不难,atoi就好了),这里面没有太复杂的逻辑,需要注意的就是其中数组、对象解析,因为数组和对象的解析需要递归去实现。
在我的简单实现里面用了单例去实现,为什么要用单例,纯粹是因为心血来潮为了实现单例去实现的,其实好像不太需要,不过因为想尝试一下单例,所以就这么去实现了。

JSON::Writer

看了一些资料中写着Writer类是通过纯虚类实现的,并且在写的过程中也确实遇到了需要纯虚类的情况,其实就是由于Value类里面需要提供asString()styledWrite()接口,但是这些接口在Writer里面肯定会去实现,如果重复写的话肯定会代码冗余,但是Writer又需要Value提供数据来转换为字符串,因此需要纯虚类来搞定这个事情。
为什么要写成纯虚类参考这里链接
由于Writer是纯虚类,所以要调用其中定义的函数必定是通过子类,Jsoncpp提供了两个子类FastWriterStyleWriter

FastWriter

FastWriter顾名思义很快,因为他不需要格式化,没有加tab的格式,所以他很快,但是我写的FastWriter我也不知道他快不快,不过Jsoncpp中叫这名字那我就叫这个吧。

StyleWriter

从名字上也可以很清楚知道这个提供的是一个格式化输出方法的类,格式化需要注意的地方也就是对tab的控制,我对tab处理用的是用了四个空格而不是\t,其实我是写博客的时候才想到\t的,影响不大。

实现FastWriter也好,StyleWriter也好,转double、字符串、布尔值都一样,因此这些统一放在基类里面实现,子类只实现三个函数——write()convert_array()convert_object()

接口名称不统一是我一开始觉得自己写的和vector的接口统一用convert_value()一样,然后后面和Jsoncpp统一接口之后之前写的就没改成convertValue()

序列化部分(Reader)

类的结构确定好了,我首先写的是解析部分,Value内当前只提供了存储,不提供其他增删查改的接口,首先把解析实现了。
value

空值、布尔值

空值、布尔值最好处理,由于可能传入的Json对象可能有空格,所以不能简单的用string的operator=处理,用C库的strncmp()很容易就处理了。

number

int和uint用atoi()处理,而double则用strtod()处理,处理前需要先检查一下数字是否合法,因为数字有可能不合法,因此需要我们判断一下,再去利用库函数转换。检查根据下图去检查是否合法,第一个符号,第一个值为0怎么处理,之后小数点处理,指数E和e,E之后的符号,数字判断,合法才去转换,这些都需要我们去做。
number

字符串

字符串需要注意的就是转义字符和UTF-8编码
string

转义字符

字符串需要处理的首先是转义字符,这个比较简单,C语言中读取转义字符是这么判断的:

  1. 首先是\说明是转义字符
  2. 然后读入n表示是\n换行

这个在Json文本中也类似,因为Json文本中也是用\n表示的,只不过我们利用C/C++去处理这个转义文本,在C字符串中是\\n那么我们处理是:

  1. 首先还是读入\,只不过在C字符串中,我们读入的肯定是个\\表示反斜杠
  2. 然后跟了一个n,这个时候我们就在字符串后追加一个\n就好了

其实就是把\n当做两个字符处理,当我们读到\\ + n就表示换行,其余处理类似。

UTF-8转换

当时看懂了,写完就忘了。。。。
大家点击这里看这篇教程中关于UTF-8的编码

array

数组我选择用vector存储,存储过程没有什么比较复杂的,因为里面每一个对象都是一个Value因此,我们要是碰到之前我们写过的对象,只需要调用之前写的方法,然后得到一个解析好的Value,只需要push_back就ok了,注意读取的时候格式就好了。
array

object

对象解析同数组类似,注意格式,key值用解析字符串的方法解析,中间跳过:,后面的值交给对应的解析去解析,之后insert(),然后跳过一个,,也没啥大问题。
object

反序列化输出(Writer部分)

空值、布尔值、string

。。。不写了

number

这里我使用的是sprint输入到数组中,数组足够精度就行,不需要太大,然后用这个数组去创建string返回。

array、object

这里看是否需要格式化,如果不需要格式化,那么只需要在最开始和最后分别加上{}[],中间加上,:即可,比较好控制,中规中矩,中间的数组或对象交给其他去处理就好了。

如果需要格式化,我是使用一个tab_count去控制,每当处理一个数组,tab_count++,处理结束就--,输出可能和标准不一样,我觉得顺眼就OK了。

其他接口

其他接口大部分是在Value类内,Value类内提供一些增加、删除、查找、修改的接口,提供判断接口,这个还是比较简单的,因为用的是C++,可以通过重载operator=就能实现值得修改,如果是对象可以提供一个类似map的接口,重载operator[],返回引用也可以通过=修改。

学习收获

  1. gdb调试比以前熟练了
  2. 内存泄漏检测,摘抄于此,以备复习
	valgrind --leak-check=full  ./leptjson_test
  1. 学会了简单的测试单元的编写
  2. 熟悉了C++的Json库
  3. 大概了解了代码重构
  4. 实际写了一下单例模式

小结

本文是记录一下我学习的过程,以便这之后复习项目用,同时也是为其他学习Json库的同学提供一个思路,我是怎么想的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++的Json库的简单实现 的相关文章

  • 创建 DirectoryEntry 实例以供测试使用

    我正在尝试创建 DirectoryEntry 的实例 以便可以使用它来测试将传递 DirectoryEntry 的一些代码 然而 尽管进行了很多尝试 我还是找不到实例化 DE 并初始化它的 PropertyCollection 的方法 我有
  • Func 方法参数的首选命名约定是什么?

    我承认这个问题是主观的 但我对社区的观点感兴趣 我有一个缓存类 它采用类型的缓存加载器函数Func
  • 模板类的不明确多重继承

    我有一个真实的情况 可以总结为以下示例 template lt typename ListenerType gt struct Notifier void add listener ListenerType struct TimeListe
  • 如何在没有 Control.Invoke() 的情况下从后台线程修改控件属性

    最近 我们遇到了一些旧版 WinForms 应用程序 我们需要更新一些新功能 在专家测试该应用程序时 发现一些旧功能被破坏 无效的跨线程操作 现在 在您认为我是新手之前 我确实有一些 Windows 窗体应用程序的经验 我不是专家 但我认为
  • SSH 主机密钥指纹与模式 C# WinSCP 不匹配

    我尝试通过 WinSCP 使用 C 连接到 FTPS 服务器 但收到此错误 SSH 主机密钥指纹 与模式不匹配 经过大量研究 我相信这与密钥的长度有关 当使用 服务器和协议信息 下的界面进行连接时 我从 WinSCP 获得的密钥是xx xx
  • 如何在我的应用程序中使用 Windows Key

    Like Windows Key E Opens a new Explorer Window And Windows Key R Displays the Run command 如何在应用程序的 KeyDown 事件中使用 Windows
  • 为什么禁止在 constexpr 函数中使用 goto?

    C 14 对你能做什么和不能做什么有规则constexpr功能 其中一些 没有asm 没有静态变量 看起来相当合理 但标准也不允许goto in constexpr功能 即使它允许其他控制流机制 这种区别背后的原因是什么 我以为我们已经过去
  • C# 中值类型和引用类型有什么区别? [复制]

    这个问题在这里已经有答案了 我知道一些差异 值类型存储在堆栈上 而引用类型存储在托管堆上 值类型变量直接包含它们的值 而引用变量仅包含对托管堆上创建的对象位置的引用 我错过了任何其他区别吗 如果是的话 它们是什么 请阅读 堆栈是一个实现细节
  • 使用 C# 在 WinRT 中获取可用磁盘空间

    DllImport kernel32 dll SetLastError true static extern bool GetDiskFreeSpaceEx string lpDirectoryName out ulong lpFreeBy
  • 如何针对 Nancy 中的 Active Directory 进行身份验证?

    这是一篇过时的文章 但是http msdn microsoft com en us library ff650308 aspx paght000026 step3 http msdn microsoft com en us library
  • c 中的错误:声明隐藏了全局范围内的变量

    当我尝试编译以下代码时 我收到此错误消息 错误 声明隐藏了全局范围内的变量 无效迭代器 节点 根 我不明白我到底在哪里隐藏或隐藏了之前声明的全局变量 我怎样才能解决这个问题 typedef node typedef struct node
  • 如何为 Jackson 编写一个包罗万象的(反)序列化器

    当您提前知道类型时 编写自定义序列化器非常容易 例如 MyType一个人可以写一个MyTypeSerializer extends StdSerializer
  • 如何在 Team Foundation 上强制发表有意义的签入评论?

    我有一个开发团队有一个坏习惯 他们写道poor签入评论 当我们必须在团队基础上查看文件的历史记录时 这使得它成为一场噩梦 我已经启用了变更集评论政策 这样他们甚至可以在签到时留下评论 否则他们不会 我们就团队的工作质量进行了一些讨论 他们很
  • .NET 选项将视频文件流式传输为网络摄像头图像

    我有兴趣开发一个应用程序 它允许我从 xml 构建视频列表 包含视频标题 持续时间等 并将该列表作为我的网络摄像头流播放 这意味着 如果我要访问 ustream tv 或在实时通讯软件上激活我的网络摄像头 我的视频播放列表将注册为我的活动网
  • 检查 url 是否指向文件或页面

    我们需要以下内容 如果文件确实是文件 则从 URL 下载该文件 否则 如果它是一个页面 则什么也不做 举个简单的例子 我有以下命令来下载文件 My Computer Network DownloadFile http www wired c
  • 什么是 C 语言的高效工作流程? - Makefile + bash脚本

    我正在开发我的第一个项目 该项目将跨越多个 C 文件 对于我的前几个练习程序 我只是在中编写了我的代码main c并使用编译gcc main c o main 当我学习时 这对我有用 现在 我正在独自开展一个更大的项目 我想继续自己进行编译
  • 作为字符串的动态属性名称

    使用 DocumentDB 创建新文档时 我想设置属性名称动态地 目前我设置SomeProperty 像这样 await client CreateDocumentAsync dbs db colls x new SomeProperty
  • char指针或char变量的默认值是什么[重复]

    这个问题在这里已经有答案了 下面是我尝试打印 char 变量和指针的默认值 值的代码 但无法在控制台上看到它 它是否有默认值或只是无法读取 ASCII 范围 include
  • GDK3/GTK3窗口更新的精确定时

    我有一个使用 GTK 用 C 语言编写的应用程序 尽管该语言对于这个问题可能并不重要 这个应用程序有全屏gtk window与单个gtk drawing area 对于绘图区域 我已经通过注册了一个刻度回调gtk widget add ti
  • 将变量分配给另一个变量,并将一个变量的更改反映到另一个变量中

    是否可以将一个变量分配给另一个变量 并且当您更改第二个变量时 更改会瀑布式下降到第一个变量 像这样 int a 0 int b a b 1 现在 b 和 a 都 1 我问这个问题的原因是因为我有 4 个要跟踪的对象 并且我使用名为 curr

随机推荐

  • Unity3D C#数学系列之矩阵基础

    目录 1 引言 2 矩阵的作用 3 矩阵的乘法 3 1 矩阵与标量的乘法 3 2 矩阵与矩阵的乘法 4 特殊矩阵 4 1 方块矩阵 4 1 1 对角矩阵 4 1 2 单位矩阵 4 2 转置矩阵 4 3 逆矩阵 4 4 正交矩阵 4 4 列矩
  • 漫游Kafka之过期数据清理

    Kafka将数据持久化到了硬盘上 允许你配置一定的策略对数据清理 清理的策略有两个 删除和压缩 数据清理的方式 删除 log cleanup policy delete启用删除策略 直接删除 删除后的消息不可恢复 可配置以下两个策略 清理超
  • Linux驱动:应用程序open如何调用到驱动程序的open函数

    字符设备文件的打开流程 相关结构体 流程涉及相关结构体如下 struct inode dev t i rdev const struct file operations i fop former gt i op gt default fil
  • 广义线性模型 matlab,基于Matlab的广义线性模型建模

    万方数据 2 黔东南民族师范高等专科学校学报 2006年12月 数向量 link 必须与gl lfit 中使用的相同 yfit就是预测值 yfit dlow d叫 glmval 6 x liIlI stats clev 返回预测值的置信界
  • 技术小白的渗透测试都是静悄悄的

    无意间发现一个phpstudy2014探针界面 决定尝试一下弱口令 万一可以登录就可以直接写入木马了 目录扫描得到phpmyadmin页面 root root登录成功 进入里面 查看日志是否开启 发现开启 qiexi 查看日志文件保存路径
  • datasource dbcp 数据源_通过 XML 实现 DataSource(数据源)注入

    这里介绍Spring提供的3种通过Xml实现DataSource 数据源 注入的方式 使用Spring 自带的DriverManagerDataSource 使用DBCP连接池和使用Tomcat提供的JNDI 下面分 别来进行介绍 1使用S
  • sh: 1: webpack-dev-server: Permission denied

    npm run dev一个项目时出现了如标题的错误 提示权限错误 我没有安装webpack dev server这个模块 也不知道之前有没有安装webpack 索性一块安装 命令行全局安装webpack webpack dev server
  • App自动化测试

    windows下搭建python appium环境 搭建过程步骤如下 安装jdk并配置好环境变量 jdk版本1 8以上 安装android sdk并配置好环境变量 具体步骤见 Android Studio安装 推荐使用这种方法安装SDK 环
  • 华硕P10S-M主板组装服务器-raid配置方法

    组装服务器磁盘阵列 阵列卡型号 P10S M 打开BIOS界面 选择Advanced 找到SATA Mode Selection选项 选择RAID磁盘阵列选项 保存后重启 开机界面按CTRL I进入磁盘阵列卡配置界面 进入磁盘阵列 进入Cr
  • Python入门(一)·环境配置与python基础

    这篇博客记录我的python入门学习过程 使用阿里云服务器ubuntu 18 04 关于Linux的入门参照上一篇博客 学习使用的视频来自B站 黑马程序员python教程 python简介 它是作者基于C语言编辑的解释器 最大的优点是开源得
  • BigDecimal的用法详解(保留两位小数,四舍五入,数字格式化,科学计数法转数字,数字里的逗号处理)

    一 简介 Java在java math包中提供的API类BigDecimal 用来对超过16位有效位的数进行精确的运算 双精度浮点型变量double可以处理16位有效数 在实际应用中 需要对更大或者更小的数进行运算和处理 float和dou
  • 推荐一款微软出品的开发神器,体验不输IDEA!

    最近微软的开发工具VSCode频繁更新Java支持 又是支持SpringBoot 又是支持Lombok 让我不禁好奇VSCode是不是也能胜任Java开发了 于是抽空体验了一把 确实完全可以胜任 Java开发者又有了新选择 不仅好用而且开源
  • 深度学习------CNN实现验证码和猫狗数据集

    1 卷积神经网络基本操作 import tensorflow as tf import numpy as np import os import matplotlib pyplot as plt import random def read
  • AI标注工具Labelme和LabelImage Labelme和LabelImage集成工具

    在AI数据标注过程中 难免会使用到标注工具 常用的工具无非是Labelme和LabelImage Labelme是标注目标轮廓 而LabelImage则是标注目标的区域 然而使用原生态的工具 需要用到python命令行 十分麻烦 为了方便大
  • C++ ——Qt的信号和槽的详解

    1 概述 信号槽是 Qt 框架引以为豪的机制之一 所谓信号槽 实际就是观察者模式 当某个事件发生之后 比如 按钮检测到自己被点击了一下 它就会发出一个信号 signal 这种发出是没有目的的 类似广播 如果有对象对这个信号感兴趣 它就会使用
  • pycharm安装需要java_安装pycharm遇到的坑

    第三周开始接触python了 结果第一步装pycharm时就遇到了坑 正常安装完成后点运行时出现错误 No JVM installation found 助教说这是缺少jdk java程序支持包 需要在网上找个最新的安装并配置下path路径
  • 【第24篇】CenterNet2论文解析,COCO成绩最高56.4mAP

    文章目录 摘要 1 简介 2 相关工作 3 准备工作 4 两阶段检测的概率解释 5 构建一个概率两级检测器 6 结果 6 1 消融研究 6 2 大词汇检测 七 结论 摘要 https arxiv org abs 2103 07461 我们开
  • 开源License的类型

    如今 Stallman率先推出的GPL已经进入到第三个版本 GNU GPLv3 且这只是几十种开源License类型中的一种 开源组织OSI 是一个在1998年成立的 为了推广开源程序和规范术语使用的组织 它已经批准了80多种开源许可证 这
  • JS数组方法&&es5数组新增方法

    1 unshift 给数组的开头添加一个或多个元素 数组名 unshift 一个值或多个值 返回添加以后的新数组的长度 2 push 给数组的末尾添加一个或多个元素 数组 push 一个值或多个值 返回新数组的长度 3 shift 给数组的
  • C++的Json库的简单实现

    我的Json库实现 Json 实现Json 我的源码 点这里 https github com jo qzy MyJson 和效果图 Json库中的类实现 JSON Value类 JSON Reader JSON Writer FastWr