[STJson]一个.Net开源json解析库

news2024/11/27 5:27:17

创作背景

项目地址: https://debugst.github.io/STJson

在开发过程中难免会遇到需要处理json的时候,但是.Net中自带的库似乎有点一言难尽啊。最后虽然找到了Newstonsoft.Json感觉还不错,但是还是觉得有些不如意的地方,它的功能虽然强大但是一些操作也过于复杂让我并不是很喜欢,而傲娇的我又喜欢自己动手。于是就开发了STLib.Json,也尽可能的让他保持最简单的使用方式。

目前内置已处理的数据类型:

.NetSTJsonValueType.NetSTJsonValueType
byteLongsbyteLong
shortLongushortLong
intLonguintLong
longLongulongLong
floatDoubledoubleDouble
decimalDoubleboolBoolean
charStringstringString
DateTimeStringenumLong or String
PointArrayPointFArray
SizeArraySizeFArray
RectangleArrayRectangleFArray
ColorArrayDataTableObject
ArrayArrayICollectionArray
IDectionaryObjectobjectObject

STLib.Json与其他json解析库有点不同,并没有类似JObjectJArray之类的对象,仅仅只有一个STJson。我不是很理解为什么要搞那么多对象,为什么不能用一个对象解决呢?无论是对象还是数组它们不都是json数据格式吗?可能开发者需要更具对象是否是数组什么的单独做一些特别的处理,但是我提供了STJson.ValueType来判断当前json是什么类型。

var json_1 = new STJson();
Console.WriteLine("[json_1] - " + json_1.IsNullObject + " - " + json_1.ValueType);

var json_2 = STJson.New();
json_2.SetItem("key", "value");
Console.WriteLine("[json_2] - " + json_2.IsNullObject + " - " + json_2.ValueType);

var json_3 = new STJson();
json_3.Append(1, 2, 3);
Console.WriteLine("[json_3] - " + json_3.IsNullObject + " - " + json_3.ValueType);

var json_4 = new STJson();
json_4.SetValue(DateTime.Now);
Console.WriteLine("[json_4] - " + json_4.IsNullObject + " - " + json_4.ValueType);

var json_5 = STJson.CreateArray();          // made by static function
Console.WriteLine("[json_5] - " + json_5.IsNullObject + " - " + json_5.ValueType);

var json_6 = STJson.CreateObject();         // made by static function
Console.WriteLine("[json_6] - " + json_6.IsNullObject + " - " + json_6.ValueType);

var json_7 = STJson.FromObject(12);         // made by static function
Console.WriteLine("[json_3] - " + json_7.IsNullObject + " - " + json_7.ValueType);
/*******************************************************************************
 *                                [output]                                     *
 *******************************************************************************/
[json_1] - True - Undefined
[json_2] - False - Object
[json_3] - False - Array
[json_4] - False - Datetime
[json_5] - False - Array
[json_6] - False - Object
[json_7] - False - Long

STJson

正如上面所看到的STJson是一个中间数据类型,它既不是string也不是object,而是介于它们之间的数据类型,它可以非常灵活的被使用。

var st_json = new STJson()
    .SetItem("number", 0)               // SetItem 返回自身
    .SetItem("boolean", true)
    .SetItem("string", "this is string")
    .SetItem("datetime", DateTime.Now)
    .SetItem("array_1", STJson.CreateArray(123, true, "string"))
    .SetItem("array_2", STJson.FromObject(new object[] { 123, true, "string" }))
    .SetItem("object", new { key = "this is a object" })
    .SetItem("null", obj: null);
st_json.SetKey("key").SetValue("this is a test");
Console.WriteLine(st_json.ToString(4)); // 4 -> 用4个空格格式化
/*******************************************************************************
 *                                [output]                                     *
 *******************************************************************************/
{
    "number": 0,
    "boolean": true,
    "string": "this is string",
    "datetime": "2023-04-22T21:12:30.6109410+08:00",
    "array_1": [
        123, true, "string"
    ],
    "array_2": [
        123, true, "string"
    ],
    "object": {
        "key": "this is a object"
    },
    "null": null,
    "key": "this is a test"
}

刚才在上面提到STJson既可以是object也可以是array,当时当执行var st_json = new STJson()时,st_json为空元素,即st_json.IsNullObject = true。因为此时无法确定st_json对象还是数组或者是

ValuteType = Array时,无法调用SetItem(..),当ValueType = Object时,无法调用Append(..)。但是通过SetValue(..)可强制改变ValueType

序列化

通过上面的例子或许你已经知道怎么将一个对象转换为string,通过STJson.FromObject(object).ToString(..)即可,但是有没有可能,其实不用这么麻烦的?比如:STJson.Serialize(..)就可以了???

事实上STJson.Serialize(..)的效率会更好,因为它是直接将对象转换为字符串,而不是转换成STJson再转换成字符串。

Console.WriteLine(STJson.Serialize(new { key = "this is test" }));
/*******************************************************************************
 *                                [output]                                     *
 *******************************************************************************/
{"key":"this is test"}

当然你可以有个更友好的输出格式:

Console.WriteLine(STJson.Serialize(new { key = "this is test" }, 4));
/*******************************************************************************
 *                                [output]                                     *
 *******************************************************************************/
{
    "key": "this is test"
}

事实上格式化输出是通过调用静态函数STJson.Format(..)完成的。如果你觉得不喜欢作者内置的格式化风格,完全可以自己写一个格式化的函数。或者去修改源码文件STJson.Statics.cs:Format(string,int)

反序列化

事实上代码并不会直接将string转换为object。因为在那之前必须先对字符串进行解析,确保它是一个正确格式的json。但是做完这个过程的时候已经得到一个STJson对象了。最后将STJson再转换为object

所以你会在源代码STLib.Json.Converter中看到如下文件:

ObjectToSTJson.cs ObjectToString.cs STJsonToObject.cs StringToSTJson.cs

里面并没有StringToObject.cs文件,而STJson.Deserialize(..)的源码如下:

public static T Deserialize<T>(string strJson, ..) {
    var json = StringToSTJson.Get(strJson, ..);
    return STJsonToObject.Get<T>(json, ..);
}

如何将字符串转换为对象,相信作者不用说明读者也应该知道如何处理,但是这里值得说明的是,STJson可以附加到对象中,实现局部更新。

public class TestClass {
    public int X;
    public int Y;
}

TestClass tc = new TestClass() {
    X = 10,
    Y = 20
};
STJson json_test = new STJson().SetItem("Y", 100);
STJson.Deserialize(json_test, tc);
Console.WriteLine(STJson.Serialize(tc));
/*******************************************************************************
 *                                [output]                                     *
 *******************************************************************************/
 {"X":10,"Y":100}

关于其他的一些用法如STJsonConverter,自定义转换器等,请自行查看教程文档。这里仅仅是对项目功能做一些基本介绍。

测试数据

[{
    "name": "Tom", "age": 16, "gender": 0,
    "hobby": [
        "cooking", "sing"
    ]
},{
    "name": "Tony", "age": 16, "gender": 0,
    "hobby": [
        "game", "dance"
    ]
},{
    "name": "Andy", "age": 20, "gender": 1,
    "hobby": [
        "draw", "sing"
    ]
},{
    "name": "Kun", "age": 26, "gender": 1,
    "hobby": [
        "sing", "dance", "rap", "basketball"
    ]
}]

var json_src = STJson.Deserialize(System.IO.File.ReadAllText("./test.json"));

此数据将在下面的案例中使用到。之后出现的json_src则为以上数据。

STJsonPath

在源码STJsonExtension.cs中对STJson的功能进行了扩展,里面集成一些STJsonPath的功能。所以在STJson的原始代码中并没有对STJsonPath的依赖,STJson可独立使用。但STJsonPath作为STJson的辅助类,需依赖STJson

目前STJsonPath支持下列选择器:

tokennote
$根节点选择器,可视作代表根节点对象。
@当前元素选择器,在遍历过程中指代当前被遍历的元素。
*通配符,表示可以代表任何一个节点。
.<name>子节点选择器,指定子节点的key
深度选择器,表示可以是任意路径。
[‘<name>’(,‘<name>’)]列表选择器,指定子节点的key集合。
[<number>(,<number>)]列表选择器,指定子节点的index集合。
[Start:End:Step]切片选择器,用于指定索引区间。
[(<expression>)]表达式选择器,用于输入一个运算表达式,并将结果作为索引继续向下选择。
[?(<expression>)]表达式选择器,用于输入一个运算表达式,并将结果转换为布尔值,决定是否继续选择。

通过以下方式可以构建一个STJsonPath

// var jp = new STJsonPath("$[0]name");
// var jp = new STJsonPath("$[0].name");
var jp = new STJsonPath("[0]'name'"); // 以上方式均可以使用 $不是必须的
Console.WriteLine(jp.Select(json_src));
/*******************************************************************************
 *                                [output]                                     *
 *******************************************************************************/
["Tom"]

当然在STJson中的扩展函数中已经集成STJsonPath,可以通过下面的方式直接使用:

// var jp = new STJsonPath("[0].name");
// Console.WriteLine(json_src.Select(jp));
Console.WriteLine(json_src.Select("[0].name")); // 内部动态构建 STJsonPath
/*******************************************************************************
 *                                [output]                                     *
 *******************************************************************************/
["Tom"]

STJsonPath以数组的方式返回数据,其返回值是STJson而不是List<STJson>STJson也可以是数组对象。

$开头对于STJsonPath来说并不是必须的,且内部会移除掉开头的$或者@$ @仅在表达式中作为对象的变量使用。

可能与其他JsonPath不同STJsonPath可以支持很复杂的表达式,为此特地编写了一个语法分析器STJsonPathParser.cs有兴趣的小伙伴可以自行查看代码。并且STJsonPath中内置了十几个内置函数可以调用。

运算符:

&& ||
< <= > >= == != re
& | << >> ^ ~
+ -
* / %
in nin anyof
!

优先级从上往下依次递增。

operatornotee.g
re正则表达式[?(@.name re ‘un’)]
in左边的值或数组包含在右边的数组中[?(@.age in [16,20])]
nin左边的值或数组不包含在右边的数组中[?(@.hobby nin [‘sing’,‘draw’])]
anyof左边的值或数组和右边的数组存在交集[?(@.hobby anyof [‘sing’,‘draw’])]

内置函数:

returnsignaturenote
stringtypeof(+n)获取数据类型。
stringstr(+n)转换为字符串。
stringupper(+n)转换为大写。
stringlower(+n)转换为小写。
longlen(+n)获取字符串或者数组长度。
longlong(+n)转换为整数。
doubledouble(+n)转换为浮点数。
long or doubleabs(+n)获取绝对值。
longround(+n)四舍五入。
longceil(+n)向上取整。
long or doublemax(+1)求最大值。
long or doublemin(+1)求最小值。
long or doubleavg(+1)求平均值。
long or doublesum(+1)求总和。
stringtrim(+n)裁切字符串两端的指定字符。
stringtrims(+n)裁切字符串开始的指定字符。
stringtrime(+n)裁切字符串末尾的指定字符。
string_arraysplit(+1)拆分字符串。
long or stringtime(+n)获取或格式化时间戳。

STJsonPath支持自定义函数扩张,详细请自行查看教程文档。

选中name中包含字母ku的元素:

//Console.WriteLine(json_src.Select("*.[?(@.name == 'kun')]").ToString(4));
Console.WriteLine(json_src.Select("*.[?(@.name re '(?i)ku')]").ToString(4));
/*******************************************************************************
 *                                [output]                                     *
 *******************************************************************************/
[
    {
        "name": "Kun",
        "age": 26,
        "gender": 1,
        "hobby": [
            "sing", "dance", "rap", "basketball"
        ]
    }
]

(?i)中的i表示忽略大小写,其正则表达式以.NetRegex为标准。(?...)开头则表示设置匹配模式。至于匹配模式自行查阅相关资料。

测试表达式

可能读者并不了解表达式在内部是如何被执行了并且会输出什么样的结果,作者提供了一个静态测试函数TestExpression()可用于调试表达式。若有什么不明白的地方测试一下就会看到过程及结果。

Console.WriteLine(STJsonPath.TestExpression(
    null,           // [STJson] 用于替代表达式中出现的 $
    null,           // [STJson] 用于替代表达式中出现的 @
    "1+2+3"         // 表达式文本
    ).ToString(4));
/*******************************************************************************
 *                                [output]                                     *
 *******************************************************************************/
{
    "type": "expression",
    "parsed": "{1 + 2 + 3}",        // 格式化后的文本 {}表示此部分需要单独执行 如: [1, {1+1}, 3]
    "polish": [
        "1", "2", " + ", "3", " + " // 逆波兰方式排列
    ],
    "steps": [                      // 执行步骤
        {
            "type": "excute",
            "operator": "+",
            "get_left_token": {     // 计算操作符左边元素的值,表达式左边也可能是一个表达式
                "parsed": "1",
                "type": "value",
                "result": {
                    "value_type": "Long",
                    "text": "1"
                }
            },
            "get_right_token": {
                "parsed": "2",
                "type": "value",
                "result": {
                    "value_type": "Long",
                    "text": "2"
                }
            },
            "result": {             // 该步骤执行结果
                "value_type": "Long",
                "text": "3"
            }
        }, {
            "type": "excute",
            "operator": "+",
            "get_left_token": {     // 此时操作符左边的元素为上一步的计算结果
                "parsed": "3",
                "type": "value",
                "result": {
                    "value_type": "Long",
                    "text": "3"
                }
            },
            "get_right_token": {
                "parsed": "3",
                "type": "value",
                "result": {
                    "value_type": "Long",
                    "text": "3"
                }
            },
            "result": {
                "value_type": "Long",
                "text": "6"
            }
        }
    ],
    "check_result": {               // 清空波兰表达式数据栈,确定最终输出结果。
        "parsed": "6",
        "type": "value",
        "result": {
            "value_type": "Long",
            "text": "6"
        }
    },
    "return": {                     // 最终返回值
        "value_type": "Long",
        "text": "6",
        "bool": true                // 如果用作布尔表达式则转换为 true
    }
}

如果过程不重要,仅仅是想看执行结果。

Console.WriteLine(STJsonPath.TestExpression(
    null,           // [STJson] 用于替代表达式中出现的 $
    null,           // [STJson] 用于替代表达式中出现的 @
    "1+2+3"         // 表达式文本
    ).SelectFirst("return").ToString(4));
/*******************************************************************************
 *                                [output]                                     *
 *******************************************************************************/
{
    "return": {                     // 最终返回值
        "value_type": "Long",
        "text": "6",
        "bool": true                // 如果用作布尔表达式则转换为 true
    }
}

ParsedToken

GetParsedTokens()用于获取当前STJsonPath得字符串在内部是如何被解析,且以STJson方式输出。如果你也想编写一个解析器,说不定可以给你提供一些思路。

Console.WriteLine(
    new STJsonPath("$..[?(matches(@.name,'u').count == 1)]").GetParsedTokens().ToString(2)
    );
/*******************************************************************************
 *                                [output]                                     *
 *******************************************************************************/
{
  "type": "entry",
  "parsed": "[..]{matches({[@]['name']}, {'u'}) == 1}",
  "items": [
    {
      "type": "selector_item",
      "item_type": "Depth",
      "value": ".."
    }, {
      "type": "expression",
      "parsed": "{matches({[@]['name']}, {'u'}) == 1}",
      "items": [
        {
          "type": "function",
          "parsed": "matches({[@]['name']}, {'u'})",
          "name": "matches",
          "args": {
            "parsed": "({[@]['name']}, {'u'})",
            "items": [
              {
                "type": "expression",
                "parsed": "{[@]['name']}",
                "items": [
                  {
                    "type": "expression_item",
                    "item_type": "selector",
                    "items": [
                      {
                        "type": "selector_item",
                        "item_type": "Current",
                        "value": "@"
                      }, {
                        "type": "selector_item",
                        "item_type": "List",
                        "value": [
                          "name"
                        ]
                      }
                    ]
                  }
                ]
              }, {
                "type": "expression",
                "parsed": "{'u'}",
                "items": [
                  {
                    "type": "expression_item",
                    "item_type": "string",
                    "value": "u"
                  }
                ]
              }
            ]
          },
          "selector": {
            "parsed": "['count']",
            "items": [
              {
                "type": "selector_item",
                "item_type": "List",
                "value": [
                  "count"
                ]
              }
            ]
          }
        }, {
          "type": "expression_item",
          "item_type": "long",
          "value": 1
        }, {
          "type": "expression_item",
          "item_type": "operator",
          "value": "=="
        }
      ]
    }
  ]
}

其他功能

由于篇幅问题这里还有很多功能无法逐一介绍,读者可自行查看在线教程文档。比如数据聚合等功能。
https://debugst.github.io/STJson/tutorial_cn.html
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/627192.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C语言:编写代码实现,模拟用户登录情景,并且只能登录三次

题目&#xff1a; 编写代码实现&#xff0c;模拟用户登录情景&#xff0c;并且只能登录三次。 只允许输入三次密码&#xff0c; 如果密码正确则提示登录成功&#xff0c; 如果三次均输入错误&#xff0c;则退出程序。 思路&#xff1a; 总体思路&#xff1a; &#xff08;一&a…

MIAOYUN“一云多芯”解决方案获评2023西部信创优秀解决方案

6月7日&#xff0c;由工业和信息化部电子第五研究所主办的“2023西部信息技术应用创新产业生态大会” 在重庆成功举办。会上&#xff0c;2023年西部地区信息技术应用创新优秀解决方案汇编正式发布&#xff0c;成都元来云志科技有限公司&#xff08;简称“MIAOYUN”&#xff09;…

现代化 Android 开发:基础架构

作者&#xff1a;古哥E下 Android 开发经过 10 多年的发展&#xff0c;技术在不断更迭&#xff0c;软件复杂度也在不断提升。到目前为止&#xff0c;虽然核心需求越来越少&#xff0c;但是对开发速度的要求越来越高。高可用、流畅的 UI、完善的监控体系等都是现在的必备要求了。…

从零开始 Spring Boot 36:注入集合

从零开始 Spring Boot 36&#xff1a;注入集合 图源&#xff1a;简书 (jianshu.com) 在前面一篇文章从零开始 Spring Boot 27&#xff1a;IoC中&#xff0c;讨论过依赖注入集合&#xff08;Java 容器&#xff09;的内容&#xff0c;这里更深入地讨论注入集合的相关内容。 我们…

ThinkPHP5学生学术管理系统

有需要请私信或看评论链接哦 可远程调试 ThinkPHP5学生学术管理系统 一 介绍 此学生学术管理系统基于ThinkPHP5框架开发&#xff0c;数据库mysql&#xff0c;前端Amazeui。系统角色分为学生用户和管理员。学生可以对个人信息&#xff0c;发表论文&#xff0c;专利授权&#x…

chatgpt赋能python:Python快速建站的SEO(搜索引擎优化)指南

Python快速建站的SEO&#xff08;搜索引擎优化&#xff09;指南 在当今数字时代&#xff0c;任何企业都需要一个强大和有效的网站。随着多个开源和商业网站平台的出现&#xff0c;建立一个网站变得更加容易。其中一个让人充满激情的开源工具是Python&#xff0c;它是一种流行的…

06_ MySQL优化实战

1. 计算并指定索引长度 阿里开发手册&#xff1a; 强制】在 varchar 字段上建立索引时&#xff0c;必须指定索引长度&#xff0c;没必要对全字段建立索引&#xff0c;根据实际文本区分度决定索引长度。 说明&#xff1a;索引的长度与区分度是一对矛盾体&#xff0c;一般对字符…

2.4 网络设计与redis、memcached、nginx组件

目录 一、网络模块需要处理哪些事情二、reactor网络设计模型三、网络模块与业务的关系四、redis、memcached、nginx1、redis2、memcached3、ngnix4、总结 一、网络模块需要处理哪些事情 网络编程主要关注客户端与服务端交互的四个问题&#xff1a; 1、连接建立 2、消息到达 3、…

学历不代表能力,但学历不够就意味着没资格!

今年的高考报名人数再创历史新高。 据悉&#xff0c;2023年全国高考报名人数1291万人&#xff0c;比去年增加98万人。 那么&#xff0c;今年的高校毕业生人数呢&#xff1f; 据人社部统计,今年我国高校毕业生人数达到1158万&#xff0c;继2022年破千万后再创历史新高。 大家…

Vue路由到新的页面,页面的名称需要改变

如下图&#xff1a;在页面中点击“属性列表”和“参数列表”的时候&#xff0c;要路由到新的页面&#xff0c;之后页面的title不用路由中的名称&#xff0c;而是用新的名称。也就是要显示对应的按钮名称&#xff0c;这个路由地址的名称是动态的。 在旧的页面上加上&#xff1a;…

汇报演示领导都说好,只因用了Smartbi幻灯片这个功能

在日常工作中&#xff0c;定期以PPT的方式汇报工作是非常常见的需求。假设你是一位销售经理&#xff0c;每个月都要参加公司的销售会议。在会议上&#xff0c;你需要向团队和高层展示销售数据、市场趋势和业绩报告等信息。过去&#xff0c;你通常是PPT来制作演示文稿&#xff0…

链表及相关面试题

链表 单链表 特点&#xff1a; 逻辑上顺序存储&#xff0c;物理上无序存储头指针根据情况而定&#xff0c;不保存数据&#xff0c;很多操作需要头指针&#xff0c;比如原地反转链表。每个节点包含 data, Node next保存下个Node public class LinkList {public Node headern…

系统初始化加载动画逻辑以及隐藏

需求&#xff1a;进入系统默认有如下的加载界面&#xff0c;但是由于网页内嵌到了其他网页中&#xff0c;这种环境下进入时再加载就不合适&#xff0c;需要隐藏掉。 因此本文的内容逻辑为 文章目录 研究加载逻辑解决需求&#xff1a;在被内嵌时隐藏掉loading 研究加载逻辑 1.…

【SpinalHDL快速入门】3、Scala 快速入门

SpinalHDL本质上来讲是Scala语言的一个库&#xff0c;所以需要先学习Scala&#xff0c;才能在此基础上学习SpinalHDL。 文章目录 Scala 基础Scala 数据类型&#xff08;5种&#xff1a;Boolean、Int、Float、Double、String&#xff09;Scala VariablesScala FunctionsReturnRe…

Python自动化测试框架:unittest介绍

Unittest是Python中最常用的测试框架之一&#xff0c;它提供了丰富和强大的测试工具和方法&#xff0c;可以帮助开发者更好地保证代码质量和稳定性&#xff0c;本文就来介绍下Unittest单元测试框架。 1. 介绍 unittest是Python的单元测试框架&#xff0c;它提供了一套丰富的测…

2023软件测试卷出天际!!!性能测试为啥一枝独秀?

近十年是中国互联网发展最快的10年&#xff0c;互联网用户从4亿增长至10亿。面对用户量的暴增&#xff0c;用户体验就成为互联网产品最大的考验。而 影响用户体验的最重要因素就是性能。 流量为王的时代&#xff0c;性能测试是所有产品上线前必须通过的重要环节。 企业招聘性…

12米与30米TanDEM-X数字高程模型DEM数据的下载申请方法

本文介绍全球12米与30米高空间分辨率的数字高程模型&#xff08;DEM&#xff09;数据——TanDEM-X数据的下载申请方法。 Tandem-X卫星项目于2010年6月启动&#xff0c;并于2010年6月21日和2010年12月21日分别发射两颗卫星&#xff0c;即TerraSAR-X和TanDEM-X。Tandem-X卫星之间…

裸辞3个月,面试了25家公司,这难度真不一般····

上半年裁员&#xff0c;下半年裸辞&#xff0c;有不少人高呼裸辞后躺平真的好快乐&#xff01;但也有很多人&#xff0c;裸辞后的生活五味杂陈。 面试25次终于找到心仪工作 因为工作压力大、领导PUA等各种原因&#xff0c;今年2月下旬我从一家互联网小厂裸辞&#xff0c;没想到…

【Android】WMS(五)输入事件原理

输入事件原理 安卓输入事件整体流程 Android 系统是由事件驱动的&#xff0c;而 input 是最常见的事件之一&#xff0c;用户的点击、滑动、长按等操作&#xff0c;都属于 input 事件驱动&#xff0c;其中的核心就是 InputReader 和 InputDispatcher。 InputReader 和 InputD…

申请国家标准项目管理专业人员能力评级(CSPM)报名条件有哪些?

2021年10月&#xff0c;中共中央、国务院发布的《国家标准化发展纲要》明确提出构建多层次从业人员培养培训体系&#xff0c;开展专业人才培养培训和国家质量基础设施综合教育。建立健全人才的职业能力评价和激励机制。由中国标准化协会&#xff08;CAS&#xff09;组织开展的项…