• [技术干货] protobuf与JSON
    前言在数据交换和通信领域,protobuf和JSON都是被广泛使用的协议。它们各自具有独特的特点和优势,使得它们在不同的场景和需求中都有其适用的地方。本文将对这两种协议进行详细介绍,并探讨它们之间的区别以及为什么protobuf没能完全取代JSON。一、protobuf介绍protobuf,全称Protocol Buffers,是由Google开发的一种数据序列化协议(类似于XML、JSON、YAML等)。它独立于语言,独立于平台。protobuf可以将结构化数据序列化为二进制格式,从而在网络上进行传输或者将数据存储到本地。protobuf的核心思想是将数据结构以某种描述性语言(proto语言)定义后,通过protobuf的编译器生成源代码,从而很方便地将数据结构序列化为二进制流,也可以很方便地将二进制流反序列化为数据结构。protobuf的主要优势在于:高效性:由于采用二进制格式,protobuf的数据传输效率远高于文本格式的协议,如JSON。在需要频繁传输大量数据的场景下,protobuf能够显著提高系统的性能。扩展性:protobuf支持向前和向后兼容,这意味着即使数据结构发生变化,只要新旧版本之间的协议能够相互识别,就可以保证数据的正常传输。语言无关性:protobuf支持多种编程语言,包括Java、C++、Python等,这使得它能够在多种语言之间进行数据交换。二、JSON介绍JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。它基于ECMAScript的一个子集,采用完全独立于语言的文本格式来存储和表示数据。简单、清晰的层次结构使得JSON成为理想的数据交换语言。易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。JSON的主要特点包括:易读性:JSON采用文本格式,具有良好的自我描述性,使得数据易于阅读和理解。通用性:由于JSON是基于文本的,它几乎可以被所有的编程语言解析和使用,这使得它在跨语言数据交换方面非常灵活。轻量级:JSON的结构简单,没有复杂的标签和属性,这使得它在数据量较小时具有优势。三、protobuf与JSON的区别尽管protobuf和JSON都是数据交换协议,但它们之间存在一些明显的区别:格式:protobuf使用二进制格式,而JSON使用文本格式。这导致protobuf在传输效率上优于JSON,但JSON在可读性和调试方面更具优势。使用场景:protobuf更适合于需要高效数据传输和处理的场景,如大型分布式系统、游戏开发等。而JSON则更适用于需要数据易于阅读和理解的场景,如配置文件、API接口等。版本兼容性:protobuf具有更好的版本兼容性,能够处理数据结构的变化。而JSON在这方面相对较弱,需要开发者自行处理数据结构的差异。四、为什么protobuf没能取代JSON尽管protobuf在数据传输效率、版本兼容性等方面具有优势,但它并没有完全取代JSON。这主要归因于以下几点:可读性:JSON的文本格式使得数据易于阅读和理解,这对于调试和排查问题非常有帮助。而protobuf的二进制格式在这方面则显得不够友好。通用性:JSON几乎可以被所有的编程语言解析和使用,这使得它在跨语言数据交换方面非常灵活。而protobuf虽然也支持多种语言,但相比之下其通用性还是稍逊一筹。学习成本:对于初学者来说,JSON的学习成本相对较低,因为它的语法简单易懂。而protobuf则需要学习其特定的描述性语言和编译器使用方法。综上所述,protobuf和JSON各有其优势和适用场景。在选择使用哪种协议时,需要根据具体的需求和场景来权衡其优缺点。虽然protobuf在某些方面表现出色,但JSON由于其可读性和通用性等方面的优势,仍然在很多场景中占据重要地位。
  • [技术干货] mysql处理json格式的字段,一文搞懂mysql解析json数据-转载
     一、概述 1、什么是JSON 略。自行百度。  2、MySQL的JSON JSON 数据类型是 MySQL 5.7.8 开始支持的。在此之前,只能通过字符类型(CHAR,VARCHAR 或 TEXT )来保存 JSON 文档。  MySQL 8.0版本中增加了对JSON类型的索引支持。可以使用CREATE INDEX语句创建JSON类型的索引,提高JSON类型数据的查询效率。  存储JSON文档所需的空间与存储LONGBLOB或LONGTEXT所需的空间大致相同。  在MySQL 8.0.13之前,JSON列不能有非空的默认值。  JSON 类型比较适合存储一些列不固定、修改较少、相对静态的数据。MySQL支持JSON格式的数据之后,可以减少对非关系型数据库的依赖。  3、varchar、text、json类型字段的区别 这三种类型的字段,都可以存储json格式,查询起来似乎正常的json函数也能用,这三者存储json类型的数据有什么区别吗?  我们接下来测试一下。  二、JSON类型的创建 1、建表指定 CREATE TABLE `users` (   `id` int NOT NULL AUTO_INCREMENT COMMENT 'id',   `name` varchar(50) DEFAULT NULL COMMENT '名字',   `json_data` json DEFAULT NULL COMMENT 'json数据',   `info` varchar(2000) DEFAULT NULL COMMENT '普通数据',   `text` text COMMENT 'text数据',   PRIMARY KEY (`id`) ) ENGINE=InnoDB; 2、修改字段 -- 添加json字段 ALTER TABLE users ADD COLUMN `test_json` JSON DEFAULT NULL COMMENT '测试'; -- 修改字段类型为json ALTER TABLE users modify test_json JSON DEFAULT NULL COMMENT '测试'; -- 删除json字段 ALTER TABLE users DROP COLUMN test_json; 三、JSON类型的插入 1、字符串直接插入 varchar、text、json格式都支持,也可以插入更复杂的嵌套json:  -- 插入数组 insert into users(json_data) values('[1, "abc", null, true, "08:45:06.000000"]'); insert into users(info) values('[1, "abc", null, true, "08:45:06.000000"]'); insert into users(text) values('[1, "abc", null, true, "08:45:06.000000"]'); -- 插入对象 insert into users(json_data) values('{"id": 87, "name": "carrot"}'); insert into users(info) values('{"id": 87, "name": "carrot"}'); insert into users(text) values('{"id": 87, "name": "carrot"}'); -- 插入嵌套json insert into users(json_data) values('[{"sex": "M"},{"sex":"F", "city":"nanjing"}]'); insert into users(info) values('[{"sex": "M"},{"sex":"F", "city":"nanjing"}]'); insert into users(text) values('[{"sex": "M"},{"sex":"F", "city":"nanjing"}]'); 但是json格式的字段,插入时会自动校验格式,如果格式不是json的,会报错:  insert into users(json_data) values('{"id", "name": "carrot"}'); > 3140 - Invalid JSON text: "Missing a colon after a name of object member." at position 5 in value for column 'users.json_data'. 1 2 2、JSON_ARRAY()函数插入数组 -- 格式: JSON_ARRAY([val[, val] ...])  -- 使用JSON_ARRAY()函数创建数组 : [1, "abc", null, true, "08:09:38.000000"] insert into users(json_data) values(JSON_ARRAY(1, "abc", null, true,curtime())); insert into users(info) values(JSON_ARRAY(1, "abc", null, true,curtime())); insert into users(text) values(JSON_ARRAY(1, "abc", null, true,curtime())); 3、JSON_OBJECT()函数插入对象 对于 JSON 文档,KEY 名不能重复。  如果插入的值中存在重复 KEY,在 MySQL 8.0.3 之前,遵循 first duplicate key wins 原则,会保留第一个 KEY,后面的将被丢弃掉。  从 MySQL 8.0.3 开始,遵循的是 last duplicate key wins 原则,只会保留最后一个 KEY。  -- 格式: JSON_OBJECT([key, val[, key, val] ...])  -- 创建对象,一个key对应一个value : {"id": 87, "name": "carrot"} insert into users(json_data) values(json_object('id', 87, 'name', 'carrot')); insert into users(info) values(json_object('id', 87, 'name', 'carrot')); insert into users(text) values(json_object('id', 87, 'name', 'carrot')); 4、JSON_ARRAYAGG()和JSON_OBJECTAGG()将查询结果封装成json mysql> SELECT o_id, attribute, value FROM t3; +------+-----------+-------+ | o_id | attribute | value | +------+-----------+-------+ |    2 | color     | red   | |    2 | fabric    | silk  | |    3 | color     | green | |    3 | shape     | square| +------+-----------+-------+ 4 rows in set (0.00 sec)  mysql> SELECT o_id, JSON_ARRAYAGG(attribute) AS attributes     -> FROM t3 GROUP BY o_id; +------+---------------------+ | o_id | attributes          | +------+---------------------+ |    2 | ["color", "fabric"] | |    3 | ["color", "shape"]  | +------+---------------------+ 2 rows in set (0.00 sec) mysql> SELECT o_id, attribute, value FROM t3; +------+-----------+-------+ | o_id | attribute | value | +------+-----------+-------+ |    2 | color     | red   | |    2 | fabric    | silk  | |    3 | color     | green | |    3 | shape     | square| +------+-----------+-------+ 4 rows in set (0.00 sec)  mysql> SELECT o_id, JSON_OBJECTAGG(attribute, value)     -> FROM t3 GROUP BY o_id; +------+---------------------------------------+ | o_id | JSON_OBJECTAGG(attribute, value)      | +------+---------------------------------------+ |    2 | {"color": "red", "fabric": "silk"}    | |    3 | {"color": "green", "shape": "square"} | +------+---------------------------------------+ 2 rows in set (0.00 sec) 四、JSON类型的解析 1、JSON_EXTRACT()解析json 格式:JSON_EXTRACT(json_doc, path[, path] …) 其中,json_doc 是 JSON 文档,path 是路径。该函数会从 JSON 文档提取指定路径(path)的元素。如果指定 path 不存在,会返回 NULL。可指定多个 path,匹配到的多个值会以数组形式返回。  -- 解析数组 -- 取下标为1的数组值(数组下标从0开始),结果:20 SELECT JSON_EXTRACT('[10, 20, [30, 40]]', '$[1]'); -- 取多个,结果返回是一个数组,结果:[20, 10] SELECT JSON_EXTRACT('[10, 20, [30, 40]]', '$[1]', '$[0]'); -- 可以使用*获取全部,结果:[30, 40] SELECT JSON_EXTRACT('[10, 20, [30, 40]]', '$[2][*]');  -- 还可通过 [M to N] 获取数组的子集 -- 结果:[10, 20] select json_extract('[10, 20, [30, 40]]', '$[0 to 1]'); -- 这里的 last 代表最后一个元素的下标,结果:[20, [30, 40]] select json_extract('[10, 20, [30, 40]]', '$[last-1 to last]'); -- 解析对象:对象的路径是通过 KEY 来表示的。 set @j='{"a": 1, "b": [2, 3], "a c": 4}';  -- 如果 KEY 在路径表达式中不合法(譬如存在空格),则在引用这个 KEY 时,需用双引号括起来。 -- 结果: 1 4 3 select json_extract(@j, '$.a'), json_extract(@j, '$."a c"'), json_extract(@j, '$.b[1]'); -- 使用*获取所有元素,结果:[1, [2, 3], 4] select json_extract('{"a": 1, "b": [2, 3], "a c": 4}', '$.*'); -- 这里的 $**.b 匹配 $.a.b 和 $.c.b,结果:[1, 2] select json_extract('{"a": {"b": 1}, "c": {"b": 2}}', '$**.b'); json_extract解析出来的数据,可以灵活用于where、order by等等所有地方。  2、-> 箭头函数解析json column->path,包括后面讲到的 column->>path,都是语法糖,在实际使用的时候都会在底层自动转化为 JSON_EXTRACT。  column->path 等同于 JSON_EXTRACT(column, path) ,只能指定一个path。  -- 同JSON_EXTRACT insert into users(json_data) values('{"empno": 1001, "ename": "jack"}'); -- 结果:"jack" select json_data, json_data -> '$.ename' from users; 3、JSON_QUOTE()引用与JSON_UNQUOTE()取消引用 JSON_QUOTE(string),生成有效的 JSON 字符串,主要是对一些特殊字符(如双引号)进行转义。  -- 结果:"null"    "\"null\""    "[1, 2, 3]" select json_quote('null'), json_quote('"null"'), json_quote('[1, 2, 3]'); 1 2 JSON_UNQUOTE(json_val),将 JSON 转义成字符串输出。常用于使用JSON_EXTRACT()和->函数解析完之后,去除引号。 JSON_UNQUOTE()特殊字符转义表:  转义序列    由序列表示的字符 \"    双引号 \b    退格字符 \f    换页字符 \n    换行符 \r    回车符 \t    制表符 \\    反斜杠(\)字符 \uXXXX    Unicode XXXX 转UTF-8 insert into users(json_data) values('{"empno": 1001, "ename": "jack"}'); -- 字符串类型转换后会去掉引号,结果:"jack"    jack    1    0 select json_data->'$.ename',json_unquote(json_data->'$.ename'),json_valid(json_data->'$.ename'),json_valid(json_unquote(json_data->'$.ename')) from users; -- 数字类型转换并没有额外效果,结果:1001    1001    1    1 select json_data->'$.empno',json_unquote(json_data->'$.empno'),json_valid(json_data->'$.empno'),json_valid(json_unquote(json_data->'$.empno')) from users; 直观地看,没加 JSON_UNQUOTE 字符串会用双引号引起来,加了 JSON_UNQUOTE 就没有。但本质上,前者是 JSON 中的 STRING 类型,后者是 MySQL 中的字符类型,这一点可通过 JSON_VALID 来判断。  4、->>箭头解析json 同 column->path 类似,只不过其返回的是字符串,相当于将字符串的双引号去掉了,是一个语法糖,本质上是执行了JSON_UNQUOTE( JSON_EXTRACT(column, path) )。  以下三者是等价的: JSON_UNQUOTE( JSON_EXTRACT(column, path) ) JSON_UNQUOTE(column -> path) column->>path  insert into users(json_data) values('{"empno": 1001, "ename": "jack"}'); -- 结果:"jack"    jack    jack    jack select json_data->'$.ename',json_unquote(json_data->'$.ename'),json_data->>'$.ename', JSON_UNQUOTE( JSON_EXTRACT(json_data, '$.ename') ) from users; 1 2 3 五、JSON类型的查询 1、JSON_CONTAINS()判断是否包含 格式:JSON_CONTAINS(target, candidate[, path]) 判断 target 文档是否包含 candidate 文档,包含的话返回1,不包含的话返回0 如果带了path,就判断path中的数据是否等于candidate,等于的话返回1,不等于的话返回0  函数前加not可取反  SET @j = '{"a": 1, "b": 2, "c": {"d": 4}}'; SET @j2 = '{"a":1}'; -- 判断@j中是否包含@j2,结果:1 SELECT JSON_CONTAINS(@j, @j2);  SET @j2 = '1'; -- 判断@j字段中的a是否等于1,结果:1 SELECT JSON_CONTAINS(@j, @j2, '$.a'); -- 结果:0 SELECT JSON_CONTAINS(@j, @j2, '$.b');  SET @j2 = '{"d": 4}'; -- 结果:0 SELECT JSON_CONTAINS(@j, @j2, '$.a'); -- 结果:1 SELECT JSON_CONTAINS(@j, @j2, '$.c');  SET @j = '[1, "a", 1.02]'; SET @j2 = '"a"'; -- 判断@j数组中是否包含@j2,结果:1 SELECT JSON_CONTAINS(@j, @j2); 2、JSON_CONTAINS_PATH()判断 格式:JSON_CONTAINS_PATH(json_doc, one_or_all, path[, path] …) 判断指定的 path 是否存在,存在,则返回 1,否则是 0。 函数中的 one_or_all 可指定 one 或 all,one 是任意一个路径存在就返回 1,all 是所有路径都存在才返回 1。  函数前加not可取反  SET @j = '{"a": 1, "b": 2, "c": {"d": 4}}'; -- a或者e 存在一个就返回1,结果:1 SELECT JSON_CONTAINS_PATH(@j, 'one', '$.a', '$.e'); -- a和e都存在返回1,结果:0 SELECT JSON_CONTAINS_PATH(@j, 'all', '$.a', '$.e'); -- c中的d存在返回1,结果:1 SELECT JSON_CONTAINS_PATH(@j, 'one', '$.c.d');  SET @j = '[1, 4, "a", "c"]'; -- @j是一个数组,$[1]判断第二个数据是否存在,结果为1 select JSON_CONTAINS_PATH(@j, 'one', '$[1]'); -- $[11]判断第11个数据不存在,结果为0 select JSON_CONTAINS_PATH(@j, 'one', '$[11]'); 3、JSON_KEYS()获取keys 返回 JSON 文档最外层的 key,如果指定了 path,则返回该 path 对应元素最外层的 key。  -- 结果:["a", "b"] SELECT JSON_KEYS('{"a": 1, "b": {"c": 30}}'); -- 结果:["c"] SELECT JSON_KEYS('{"a": 1, "b": {"c": 30}}', '$.b'); 4、JSON_OVERLAPS()比较两个json MySQL 8.0.17 引入的,用来比较两个 JSON 文档是否有相同的键值对或数组元素,如果有,则返回 1,否则是 0。 如果两个参数都是标量,则判断这两个标量是否相等。  函数前加not可取反  -- 结果: 1    0 select json_overlaps('[1,3,5,7]', '[2,5,7]'),json_overlaps('[1,3,5,7]', '[2,6,8]');  -- 部分匹配被视为不匹配,结果:0 SELECT JSON_OVERLAPS('[[1,2],[3,4],5]', '[1,[2,3],[4,5]]');  -- 比较对象时,如果它们至少有一个共同的键值对,则结果为真。 -- 结果:1 SELECT JSON_OVERLAPS('{"a":1,"b":10,"d":10}', '{"c":1,"e":10,"f":1,"d":10}'); -- 结果:0 SELECT JSON_OVERLAPS('{"a":1,"b":10,"d":10}', '{"a":5,"e":10,"f":1,"d":20}');  -- 如果两个标量用作函数的参数,JSON_OVERLAPS()会执行一个简单的相等测试: -- 结果:1 SELECT JSON_OVERLAPS('5', '5'); -- 结果:0 SELECT JSON_OVERLAPS('5', '6');  -- 当比较标量和数组时,JSON_OVERLAPS()试图将标量视为数组元素。在此示例中,第二个参数6被解释为[6],如下所示:结果:1 SELECT JSON_OVERLAPS('[4,5,6,7]', '6');  -- 该函数不执行类型转换: -- 结果:0 SELECT JSON_OVERLAPS('[4,5,"6",7]', '6'); -- 结果:0 SELECT JSON_OVERLAPS('[4,5,6,7]', '"6"'); 5、JSON_SEARCH()返回字符串的位置 格式:JSON_SEARCH(json_doc, one_or_all, search_str[, escape_char[, path] …])  返回某个字符串(search_str)在 JSON 文档中的位置,其中, one_or_all:匹配的次数,one 是只匹配一次,all 是匹配所有。如果匹配到多个,结果会以数组的形式返回。 search_str:子串,支持模糊匹配:% 和 _ 。 escape_char:转义符,如果该参数不填或为 NULL,则取默认转义符\。 path:查找路径。  SET @j = '["abc", [{"k": "10"}, "def"], {"x":"abc"}, {"y":"bcd"}]'; -- 结果:"$[0]" SELECT JSON_SEARCH(@j, 'one', 'abc'); -- 结果:["$[0]", "$[2].x"] SELECT JSON_SEARCH(@j, 'all', 'abc'); -- 结果:null SELECT JSON_SEARCH(@j, 'all', 'ghi'); -- 结果:"$[1][0].k" SELECT JSON_SEARCH(@j, 'all', '10'); -- 结果:"$[1][0].k" SELECT JSON_SEARCH(@j, 'all', '10', NULL, '$'); -- 结果:"$[1][0].k" SELECT JSON_SEARCH(@j, 'all', '10', NULL, '$[*]'); -- 结果:"$[1][0].k" SELECT JSON_SEARCH(@j, 'all', '10', NULL, '$**.k'); -- 结果:"$[1][0].k" SELECT JSON_SEARCH(@j, 'all', '10', NULL, '$[*][0].k'); -- 结果:"$[1][0].k" SELECT JSON_SEARCH(@j, 'all', '10', NULL, '$[1]'); -- 结果:"$[1][0].k" SELECT JSON_SEARCH(@j, 'all', '10', NULL, '$[1][0]'); -- 结果:"$[2].x" SELECT JSON_SEARCH(@j, 'all', 'abc', NULL, '$[2]'); -- 结果:["$[0]", "$[2].x"] SELECT JSON_SEARCH(@j, 'all', '%a%'); -- 结果:["$[0]", "$[2].x", "$[3].y"] SELECT JSON_SEARCH(@j, 'all', '%b%'); -- 结果:"$[0]" SELECT JSON_SEARCH(@j, 'all', '%b%', NULL, '$[0]'); -- 结果:"$[2].x" SELECT JSON_SEARCH(@j, 'all', '%b%', NULL, '$[2]'); -- 结果:null SELECT JSON_SEARCH(@j, 'all', '%b%', NULL, '$[1]'); -- 结果:null SELECT JSON_SEARCH(@j, 'all', '%b%', '', '$[1]'); -- 结果:"$[3].y" SELECT JSON_SEARCH(@j, 'all', '%b%', '', '$[3]'); 6、JSON_VALUE()提取指定路径的元素 格式:JSON_VALUE(json_doc, path) 8.0.21 引入的,从 JSON 文档提取指定路径(path)的元素。 完整的语法如下所示:  JSON_VALUE(json_doc, path [RETURNING type] [on_empty] [on_error])  on_empty:     {NULL | ERROR | DEFAULT value} ON EMPTY  on_error:     {NULL | ERROR | DEFAULT value} ON ERROR 其中: RETURNING type:返回值的类型,不指定,则默认是 VARCHAR(512)。不指定字符集,则默认是 utf8mb4,且区分大小写。 on_empty:如果指定路径没有值,会触发 on_empty 子句, 默认是返回 NULL,也可指定 ERROR 抛出错误,或者通过 DEFAULT value 返回默认值。 on_error:三种情况下会触发 on_error 子句:从数组或对象中提取元素时,会解析到多个值;类型转换错误,譬如将 “abc” 转换为 unsigned 类型;值被 truncate 了。默认是返回 NULL。  -- 查找fname的值,结果为:Joe SELECT JSON_VALUE('{"fname": "Joe", "lname": "Palmer"}', '$.fname'); -- 结果:49.95 SELECT JSON_VALUE('{"item": "shoes", "price": "49.95"}', '$.price' RETURNING DECIMAL(4,2)) AS price; -- 结果:50.0 SELECT JSON_VALUE('{"item": "shoes", "price": "49.95"}', '$.price' RETURNING DECIMAL(4,1)) AS price; -- 使用RETURNING定义返回数据类型,等效于以下sql: SELECT CAST(     JSON_UNQUOTE( JSON_EXTRACT(json_doc, path) )     AS type );  mysql> select json_value('{"item": "shoes", "price": "49.95"}', '$.price1' error on empty); ERROR 3966 (22035): No value was found by 'json_value' on the specified path.  mysql> select json_value('[1, 2, 3]', '$[1 to 2]' error on error); ERROR 3967 (22034): More than one value was found by 'json_value' on the specified path.  mysql> select json_value('{"item": "shoes", "price": "49.95"}', '$.item' returning unsigned error on error) as price; ERROR 1690 (22003): UNSIGNED value is out of range in 'json_value' 7、MEMBER OF()判断是否是json数组中的元素 格式:value MEMBER OF(json_array) 在 MySQL 8.0.17引入了MEMBER OF()函数。判断 value 是否是 JSON 数组的一个元素,如果是,则返回 1,否则是 0。  函数前加not可取反  -- 结果:1 SELECT 17 MEMBER OF('[23, "abc", 17, "ab", 10]'); -- 结果:1 SELECT 'ab' MEMBER OF('[23, "abc", 17, "ab", 10]'); -- 部分匹配不代表匹配 -- 结果:0 SELECT 7 MEMBER OF('[23, "abc", 17, "ab", 10]'); -- 结果:0 SELECT 'a' MEMBER OF('[23, "abc", 17, "ab", 10]'); -- 不执行字符串类型之间的相互转换:结果:0·0 SELECT 17 MEMBER OF('[23, "abc", "17", "ab", 10]'), "17" MEMBER OF('[23, "abc", 17, "ab", 10]') -- 要将该操作符与本身是数组的值一起使用,必须将其显式转换为JSON数组。结果:1 SELECT CAST('[4,5]' AS JSON) MEMBER OF('[[3,4],[4,5]]'); -- 还可以使用JSON_ARRAY()函数执行必要的强制转换,如下所示: 结果:1 SELECT JSON_ARRAY(4,5) MEMBER OF('[[3,4],[4,5]]');  --转换,结果:1    1 SET @a = CAST('{"a":1}' AS JSON); SET @b = JSON_OBJECT("b", 2); SET @c = JSON_ARRAY(17, @b, "abc", @a, 23); SELECT @a MEMBER OF(@c), @b MEMBER OF(@c); 8、JSON_DEPTH()获取JSON最大深度 语法:JSON_DEPTH(json_doc) 返回JSON文档的最大深度。如果参数为NULL,则返回NULL。如果参数不是有效的JSON文档,则会出现错误。 对于空数组,空对象,标量值,其深度为 1。  -- 结果:1    1    1 SELECT JSON_DEPTH('{}'), JSON_DEPTH('[]'), JSON_DEPTH('true'); -- 结果:2    2 SELECT JSON_DEPTH('[10, 20]'), JSON_DEPTH('[[], {}]'); -- 结果:3 SELECT JSON_DEPTH('[10, {"a": 20}]'); 9、JSON_LENGTH()获取文档长度 语法:JSON_LENGTH(json_doc[, path])  返回 JSON 文档的长度,其计算规则如下: 1、如果是标量值,其长度为 1。 2、如果是数组,其长度为数组元素的个数。 3、如果是对象,其长度为对象元素的个数。 4、不包括嵌套数据和嵌套对象的长度。  -- 结果:3 SELECT JSON_LENGTH('[1, 2, {"a": 3}]'); -- 结果:2 SELECT JSON_LENGTH('{"a": 1, "b": {"c": 30}}'); -- 结果:1 10、JSON_TYPE()获取JSON类型 语法:JSON_TYPE(json_val) 返回 JSON 值的类型。 如果参数不是有效的JSON值,则会出现错误。  SET @j = '{"a": [10, true]}'; -- 结果:OBJECT SELECT JSON_TYPE(@j); -- 结果:ARRAY SELECT JSON_TYPE(JSON_EXTRACT(@j, '$.a')); -- 结果:INTEGER SELECT JSON_TYPE(JSON_EXTRACT(@j, '$.a[0]')); -- 结果:BOOLEAN SELECT JSON_TYPE(JSON_EXTRACT(@j, '$.a[1]')); -- 结果:NULL SELECT JSON_TYPE(NULL); -- 结果:STRING select json_type('"abc"'); -- 结果:DATETIME select json_type(cast(now() as json)); JSON类型:OBJECT(对象)、ARRAY(数组)、BOOLEAN(布尔类型)、NULL 数字类型:INTEGER(TINYINT、SMALLINT、MEDIUMINT以及INT和BIGINT标量)、DOUBLE(DOUBLE、FLOAT)、DECIMAL(MySQL、DECIMAL) 时间类型:DATETIME(DATETIME、TIMESTAMP)、DATE、TIME 字符串类型:STRING(CHAR, VARCHAR, TEXT, ENUM, SET) 二进制类型:BLOB( BINARY, VARBINARY, BLOB, BIT) 其他类型:OPAQUE  11、JSON_VALID()校验JSON格式 语法:JSON_VALID(val) 判断给定值是否是有效的 JSON 文档。 函数前加not可取反  -- 结果:1 SELECT JSON_VALID('{"a": 1}'); -- 结果:0    1 SELECT JSON_VALID('hello'), JSON_VALID('"hello"'); 1 2 3 4 六、JSON类型的修改 1、全量修改 直接使用update语句,将json数据字段全部替换。  update users set json_data = '{"a":1}'; 1 2、JSON_ARRAY_APPEND()向数组追加元素 格式:JSON_ARRAY_APPEND(json_doc, path, val[, path, val] …) 向数组指定位置追加元素。如果指定 path 不存在,则不添加。 在MySQL 5.7中,这个函数被命名为JSON_APPEND()。MySQL 8.0不再支持该名称。  SET @j = '["a", ["b", "c"], "d"]'; -- 在数组第二个元素的数组中追加1,结果:["a", ["b", "c", 1], "d"] SELECT JSON_ARRAY_APPEND(@j, '$[1]', 1); -- 结果:[["a", 2], ["b", "c"], "d"] SELECT JSON_ARRAY_APPEND(@j, '$[0]', 2); -- 结果:["a", [["b", 3], "c"], "d"] SELECT JSON_ARRAY_APPEND(@j, '$[1][0]', 3); -- 多个参数,结果:[["a", 1], [["b", 2], "c"], "d"] select json_array_append(@j, '$[0]', 1, '$[1][0]', 2, '$[3]', 3);  SET @j = '{"a": 1, "b": [2, 3], "c": 4}'; -- 往b中追加,结果:{"a": 1, "b": [2, 3, "x"], "c": 4} SELECT JSON_ARRAY_APPEND(@j, '$.b', 'x'); -- 结果:{"a": 1, "b": [2, 3], "c": [4, "y"]} SELECT JSON_ARRAY_APPEND(@j, '$.c', 'y');  SET @j = '{"a": 1}'; -- 结果:[{"a": 1}, "z"] SELECT JSON_ARRAY_APPEND(@j, '$', 'z'); 3、JSON_ARRAY_INSERT()向数组指定位置插入元素 格式:JSON_ARRAY_INSERT(json_doc, path, val[, path, val] …) 向数组指定位置插入元素。  SET @j = '["a", {"b": [1, 2]}, [3, 4]]'; -- 在下标1处添加元素x,结果:["a", "x", {"b": [1, 2]}, [3, 4]] SELECT JSON_ARRAY_INSERT(@j, '$[1]', 'x'); -- 没有100个元素,在最后插入,结果: ["a", {"b": [1, 2]}, [3, 4], "x"] SELECT JSON_ARRAY_INSERT(@j, '$[100]', 'x'); -- 结果:["a", {"b": ["x", 1, 2]}, [3, 4]] SELECT JSON_ARRAY_INSERT(@j, '$[1].b[0]', 'x'); -- 结果:["a", {"b": [1, 2]}, [3, "y", 4]] SELECT JSON_ARRAY_INSERT(@j, '$[2][1]', 'y');  -- 早期的修改会影响数组中后续元素的位置,因此同一个JSON_ARRAY_INSERT()调用中的后续路径应该考虑这一点。在最后一个示例中,第二个路径没有插入任何内容,因为在第一次插入之后,该路径不再匹配任何内容。 -- 结果:["x", "a", {"b": [1, 2]}, [3, 4]] SELECT JSON_ARRAY_INSERT(@j, '$[0]', 'x', '$[2][1]', 'y'); 4、JSON_INSERT()插入新值 格式:JSON_INSERT(json_doc, path, val[, path, val] …) 插入不存在的key的值,已经存在的不修改。 仅当指定位置或指定 KEY 的值不存在时,才执行插入操作。另外,如果指定的 path 是数组下标,且 json_doc 不是数组,该函数首先会将 json_doc 转化为数组,然后再插入新值。  SET @j = '{ "a": 1, "b": [2, 3]}'; -- a已经存在则忽略,c不存在则添加,结果:{"a": 1, "b": [2, 3], "c": "[true, false]"} SELECT JSON_INSERT(@j, '$.a', 10, '$.c', '[true, false]'); -- 上面插入的c是一个带引号的字符串,想要插入一个数组,必须进行转换,结果:{"a": 1, "b": [2, 3], "c": [true, false]} SELECT JSON_INSERT(@j, '$.a', 10, '$.c', CAST('[true, false]' AS JSON));  -- 下标0位置已经有值了,不会插入,结果:1 select json_insert('1','$[0]',"10"); -- 结果:[1, "10"] select json_insert('1','$[1]',"10"); -- 结果:["1", "2", "10"] select json_insert('["1","2"]','$[2]',"10"); 5、JSON_MERGE()合并json 格式:JSON_MERGE(json_doc, json_doc[, json_doc] …) 合并两个或多个JSON文档。JSON_MERGE_PRESERVE()的同义词;在MySQL 8.0.3中已弃用,在未来版本中可能会被删除。 推荐使用JSON_MERGE_PRESERVE()  -- 结果:[1, 2, true, false] SELECT JSON_MERGE('[1, 2]', '[true, false]'); 1 2 6、JSON_MERGE_PATCH()合并json MySQL 8.0.3 引入的,用来合并多个 JSON 文档。其合并规则如下: 1、如果两个文档不全是 JSON 对象,则合并后的结果是第二个文档。 2、如果两个文档都是 JSON 对象,且不存在着同名 KEY,则合并后的文档包括两个文档的所有元素,如果存在着同名 KEY,则第二个文档的值会覆盖第一个。  -- 不是对象,结果:[true, false] SELECT JSON_MERGE_PATCH('[1, 2]', '[true, false]'); -- 都是对象,结果:{"id": 47, "name": "x"} SELECT JSON_MERGE_PATCH('{"name": "x"}', '{"id": 47}'); -- 都不是对象,取第二个,结果:true SELECT JSON_MERGE_PATCH('1', 'true'); -- 第一个不是对象,取第二个 ,结果:{"id": 47} SELECT JSON_MERGE_PATCH('[1, 2]', '{"id": 47}'); -- 第二个覆盖第一个,结果:{"a": 3, "b": 2, "c": 4} SELECT JSON_MERGE_PATCH('{ "a": 1, "b":2 }','{ "a": 3, "c":4 }'); -- 结果:{"a": 5, "b": 2, "c": 4, "d": 6} SELECT JSON_MERGE_PATCH('{ "a": 1, "b":2 }','{ "a": 3, "c":4 }', '{ "a": 5, "d":6 }'); -- 第二个有null,会删除该key,结果:{"a": 1} SELECT JSON_MERGE_PATCH('{"a":1, "b":2}', '{"b":null}'); -- 嵌套json也可以合并,结果:{"a": {"x": 1, "y": 2}} SELECT JSON_MERGE_PATCH('{"a":{"x":1}}', '{"a":{"y":2}}');  注意区别于JSON_MERGE_PRESERVE  7、JSON_MERGE_PRESERVE()合并json MySQL 8.0.3 引入的,用来代替 JSON_MERGE。也是用来合并文档,但合并规则与 JSON_MERGE_PATCH 有所不同。 1、两个文档中,只要有一个文档是数组,则另外一个文档会合并到该数组中。 2、两个文档都是 JSON 对象,若存在着同名 KEY ,第二个文档并不会覆盖第一个,而是会将值 append 到第一个文档中。  -- 数组合并,结果:[1, 2, true, false] SELECT JSON_MERGE_PRESERVE('[1, 2]', '[true, false]'); -- 对象合并,结果:{"id": 47, "name": "x"} SELECT JSON_MERGE_PRESERVE('{"name": "x"}', '{"id": 47}'); -- 两个常量,合并为一个数组,结果:[1, true] SELECT JSON_MERGE_PRESERVE('1', 'true'); -- 对象合并到数组中,结果:[1, 2, {"id": 47}] SELECT JSON_MERGE_PRESERVE('[1, 2]', '{"id": 47}'); -- 相同的key合并到一个数组,结果:{"a": [1, 3], "b": 2, "c": 4} SELECT JSON_MERGE_PRESERVE('{ "a": 1, "b": 2 }', '{ "a": 3, "c": 4 }'); -- 结果:{"a": [1, 3, 5], "b": 2, "c": 4, "d": 6}  SELECT JSON_MERGE_PRESERVE('{ "a": 1, "b": 2 }','{ "a": 3, "c": 4 }', '{ "a": 5, "d": 6 }'); 注意区别于JSON_MERGE_PATCH()  8、JSON_REMOVE()删除元素 格式:JSON_REMOVE(json_doc, path[, path] …) 删除 JSON 文档指定位置的元素。  SET @j = '["a", ["b", "c"], "d"]'; -- 删除下标为1的元素,结果:["a", "d"] SELECT JSON_REMOVE(@j, '$[1]');  set @j = '{ "a": 1, "b": [2, 3]}'; -- 删除a元素,结果:{"b": [2, 3]} select json_remove(@j, '$.a');  set @j = '["a", ["b", "c"], "d", "e"]'; -- 删除多个元素,删除1下标之后,下标移动结果之后再删除下标2位置,结果:["a", "d"] select json_remove(@j, '$[1]','$[2]'); -- 结果:["a", "e"] select json_remove(@j, '$[1]','$[1]'); 9、JSON_REPLACE()替换元素 语法:JSON_REPLACE(json_doc, path, val[, path, val] …) 替换已经存在的值。不存在的值不做影响。  SET @j = '{ "a": 1, "b": [2, 3]}'; -- 对象替换,结果:{"a": 10, "b": [2, 3]} SELECT JSON_REPLACE(@j, '$.a', 10, '$.c', '[true, false]');  -- 数组替换,结果:[1, "a", 4, "b"] select json_replace('[1, "a", 3, "b"]', '$[2]', 4, '$[8]', 8); 10、JSON_SET()插入并替换 格式:JSON_SET(json_doc, path, val[, path, val] …) 插入新值,并替换已经存在的值。 换言之,如果指定位置或指定 KEY 的值不存在,会执行插入操作,如果存在,则执行更新操作。  注意JSON_SET、JSON_INSERT、JSON_REPLACE的区别。  SET @j = '{ "a": 1, "b": [2, 3]}'; -- 结果:{"a": 10, "b": [2, 3], "c": "[true, false]"} SELECT JSON_SET(@j, '$.a', 10, '$.c', '[true, false]'); -- 结果:{"a": 1, "b": [2, 3], "c": "[true, false]"} SELECT JSON_INSERT(@j, '$.a', 10, '$.c', '[true, false]'); -- 结果:{"a": 10, "b": [2, 3]} SELECT JSON_REPLACE(@j, '$.a', 10, '$.c', '[true, false]'); 七、其他JSON函数 1、JSON_TABLE()列转行 语法:JSON_TABLE(expr, path COLUMNS (column_list) [AS] alias) MySQL 8.0支持这样一个函数,JSON_TABLE(),从 JSON 文档中提取数据并以表格的形式返回。  完整语法如下:  JSON_TABLE(     expr,     path COLUMNS (column_list) )   [AS] alias  column_list:     column[, column][, ...]  column:     name FOR ORDINALITY     |  name type PATH string path [on_empty] [on_error]     |  name type EXISTS PATH string path     |  NESTED [PATH] path COLUMNS (column_list)  on_empty:     {NULL | DEFAULT json_string | ERROR} ON EMPTY  on_error:     {NULL | DEFAULT json_string | ERROR} ON ERROR mysql> SELECT *     ->   FROM     ->     JSON_TABLE(     ->       '[ {"c1": null} ]',     ->       '$[*]' COLUMNS( c1 INT PATH '$.c1' ERROR ON ERROR )     ->     ) as jt; +------+ | c1   | +------+ | NULL | +------+ 1 row in set (0.00 sec) select *  from    json_table(      '[{"x":2, "y":"8", "z":9, "b":[1,2,3]}, {"x":"3", "y":"7"}, {"x":"4", "y":6, "z":10}]',      "$[*]" columns(        id for ordinality,        xval varchar(100) path "$.x",        yval varchar(100) path "$.y",        z_exist int exists path "$.z",        nested path '$.b[*]' columns (b INT PATH '$')      )    ) as t; +------+------+------+---------+------+ | id   | xval | yval | z_exist | b    | +------+------+------+---------+------+ |    1 | 2    | 8    |       1 |    1 | |    1 | 2    | 8    |       1 |    2 | |    1 | 2    | 8    |       1 |    3 | |    2 | 3    | 7    |       0 | NULL | |    3 | 4    | 6    |       1 | NULL | +------+------+------+---------+------+ 5 rows in set (0.00 sec) mysql> SELECT *     -> FROM     ->   JSON_TABLE(     ->     '[{"a":"3"},{"a":2},{"b":1},{"a":0},{"a":[1,2]}]',     ->     "$[*]"     ->     COLUMNS(     ->       rowid FOR ORDINALITY,     ->       ac VARCHAR(100) PATH "$.a" DEFAULT '111' ON EMPTY DEFAULT '999' ON ERROR,     ->       aj JSON PATH "$.a" DEFAULT '{"x": 333}' ON EMPTY,     ->       bx INT EXISTS PATH "$.b"     ->     )     ->   ) AS tt;  +-------+------+------------+------+ | rowid | ac   | aj         | bx   | +-------+------+------------+------+ |     1 | 3    | "3"        |    0 | |     2 | 2    | 2          |    0 | |     3 | 111  | {"x": 333} |    1 | |     4 | 0    | 0          |    0 | |     5 | 999  | [1, 2]     |    0 | +-------+------+------------+------+ 5 rows in set (0.00 sec) mysql> SELECT *     -> FROM     ->   JSON_TABLE(     ->     '[{"x":2,"y":"8"},{"x":"3","y":"7"},{"x":"4","y":6}]',     ->     "$[*]" COLUMNS(     ->       xval VARCHAR(100) PATH "$.x",     ->       yval VARCHAR(100) PATH "$.y"     ->     )     ->   ) AS  jt1;  +------+------+ | xval | yval | +------+------+ | 2    | 8    | | 3    | 7    | | 4    | 6    | +------+------+  -- 指定path mysql> SELECT *     -> FROM     ->   JSON_TABLE(     ->     '[{"x":2,"y":"8"},{"x":"3","y":"7"},{"x":"4","y":6}]',     ->     "$[1]" COLUMNS(     ->       xval VARCHAR(100) PATH "$.x",     ->       yval VARCHAR(100) PATH "$.y"     ->     )     ->   ) AS  jt1;  +------+------+ | xval | yval | +------+------+ | 3    | 7    | +------+------+  mysql> SELECT *     -> FROM     ->   JSON_TABLE(     ->     '[ {"a": 1, "b": [11,111]}, {"a": 2, "b": [22,222]}, {"a":3}]',     ->     '$[*]' COLUMNS(     ->             a INT PATH '$.a',     ->             NESTED PATH '$.b[*]' COLUMNS (b INT PATH '$')     ->            )     ->    ) AS jt     -> WHERE b IS NOT NULL;  +------+------+ | a    | b    | +------+------+ |    1 |   11 | |    1 |  111 | |    2 |   22 | |    2 |  222 | +------+------+ mysql> SELECT *     -> FROM     ->   JSON_TABLE(     ->     '[{"a": 1, "b": [11,111]}, {"a": 2, "b": [22,222]}]',     ->     '$[*]' COLUMNS(     ->         a INT PATH '$.a',     ->         NESTED PATH '$.b[*]' COLUMNS (b1 INT PATH '$'),     ->         NESTED PATH '$.b[*]' COLUMNS (b2 INT PATH '$')     ->     )     -> ) AS jt;  +------+------+------+ | a    | b1   | b2   | +------+------+------+ |    1 |   11 | NULL | |    1 |  111 | NULL | |    1 | NULL |   11 | |    1 | NULL |  111 | |    2 |   22 | NULL | |    2 |  222 | NULL | |    2 | NULL |   22 | |    2 | NULL |  222 | +------+------+------+ mysql> SELECT *     -> FROM     ->   JSON_TABLE(     ->     '[{"a": "a_val",     '>       "b": [{"c": "c_val", "l": [1,2]}]},     '>     {"a": "a_val",     '>       "b": [{"c": "c_val","l": [11]}, {"c": "c_val", "l": [22]}]}]',     ->     '$[*]' COLUMNS(     ->       top_ord FOR ORDINALITY,     ->       apath VARCHAR(10) PATH '$.a',     ->       NESTED PATH '$.b[*]' COLUMNS (     ->         bpath VARCHAR(10) PATH '$.c',     ->         ord FOR ORDINALITY,     ->         NESTED PATH '$.l[*]' COLUMNS (lpath varchar(10) PATH '$')     ->         )     ->     )     -> ) as jt;  +---------+---------+---------+------+-------+ | top_ord | apath   | bpath   | ord  | lpath | +---------+---------+---------+------+-------+ |       1 |  a_val  |  c_val  |    1 | 1     | |       1 |  a_val  |  c_val  |    1 | 2     | |       2 |  a_val  |  c_val  |    1 | 11    | |       2 |  a_val  |  c_val  |    2 | 22    | +---------+---------+---------+------+-------+ 与表关联查询:  CREATE TABLE t1 (c1 INT, c2 CHAR(1), c3 JSON);  INSERT INTO t1 () VALUES     ROW(1, 'z', JSON_OBJECT('a', 23, 'b', 27, 'c', 1)),     ROW(1, 'y', JSON_OBJECT('a', 44, 'b', 22, 'c', 11)),     ROW(2, 'x', JSON_OBJECT('b', 1, 'c', 15)),     ROW(3, 'w', JSON_OBJECT('a', 5, 'b', 6, 'c', 7)),     ROW(5, 'v', JSON_OBJECT('a', 123, 'c', 1111)) ;  SELECT c1, c2, JSON_EXTRACT(c3, '$.*')  FROM t1 AS m  JOIN  JSON_TABLE(   m.c3,    '$.*'    COLUMNS(     at VARCHAR(10) PATH '$.a' DEFAULT '1' ON EMPTY,      bt VARCHAR(10) PATH '$.b' DEFAULT '2' ON EMPTY,      ct VARCHAR(10) PATH '$.c' DEFAULT '3' ON EMPTY   ) ) AS tt ON m.c1 > tt.at;  结果:  与表关联查询:  CREATE TABLE employees (   id INT,   details JSON );  INSERT INTO employees VALUES (1, '{"name": "John Doe", "position": "Manager"}'); INSERT INTO employees VALUES (2, '{"name": "Jane Smith", "position": "Developer"}');  SELECT name, position FROM employees, JSON_TABLE(details, '$' COLUMNS(   name VARCHAR(255) PATH '$.name',   position VARCHAR(255) PATH '$.position' )) AS emp; 2、JSON_SCHEMA_VALID()验证json 语法:JSON_SCHEMA_VALID(schema,document) 判断 document ( JSON 文档 )是否满足 schema ( JSON 对象)定义的规范要求。完整的规范要求可参考 Draft 4 of the JSON Schema specification (https://json-schema.org/specification-links.html#draft-4)。如果不满足,可通过 JSON_SCHEMA_VALIDATION_REPORT() 获取具体的原因。  它的要求如下: 1、document 必须是 JSON 对象。 2、JSON 对象必需的两个属性是 latitude 和 longitude。 3、latitude 和 longitude 必须是数值类型,且两者的大小分别在 -90 ~ 90,-180 ~ 180 之间。  mysql> SET @schema = '{     '>  "id": "http://json-schema.org/geo",     '> "$schema": "http://json-schema.org/draft-04/schema#",     '> "description": "A geographical coordinate",     '> "type": "object",     '> "properties": {     '>   "latitude": {     '>     "type": "number",     '>     "minimum": -90,     '>     "maximum": 90     '>   },     '>   "longitude": {     '>     "type": "number",     '>     "minimum": -180,     '>     "maximum": 180     '>   }     '> },     '> "required": ["latitude", "longitude"]     '>}'; Query OK, 0 rows affected (0.01 sec)  mysql> SET @document = '{     '> "latitude": 63.444697,     '> "longitude": 10.445118     '>}'; Query OK, 0 rows affected (0.00 sec)  mysql> SELECT JSON_SCHEMA_VALID(@schema, @document); +---------------------------------------+ | JSON_SCHEMA_VALID(@schema, @document) | +---------------------------------------+ |                                     1 | +---------------------------------------+ 1 row in set (0.00 sec) mysql> SET @document = '{}'; mysql> SET @schema = '{     '> "id": "http://json-schema.org/geo",     '> "$schema": "http://json-schema.org/draft-04/schema#",     '> "description": "A geographical coordinate",     '> "type": "object",     '> "properties": {     '>   "latitude": {     '>     "type": "number",     '>     "minimum": -90,     '>     "maximum": 90     '>   },     '>   "longitude": {     '>     "type": "number",     '>     "minimum": -180,     '>     "maximum": 180     '>   }     '> }     '>}'; Query OK, 0 rows affected (0.00 sec)  mysql> SELECT JSON_SCHEMA_VALID(@schema, @document); +---------------------------------------+ | JSON_SCHEMA_VALID(@schema, @document) | +---------------------------------------+ |                                     1 | +---------------------------------------+ 1 row in set (0.00 sec)  -- 建表指定check mysql> CREATE TABLE geo (     ->     coordinate JSON,     ->     CHECK(     ->         JSON_SCHEMA_VALID(     ->             '{     '>                 "type":"object",     '>                 "properties":{     '>                       "latitude":{"type":"number", "minimum":-90, "maximum":90},     '>                       "longitude":{"type":"number", "minimum":-180, "maximum":180}     '>                 },     '>                 "required": ["latitude", "longitude"]     '>             }',     ->             coordinate     ->         )     ->     )     -> ); Query OK, 0 rows affected (0.45 sec)  mysql> SET @point1 = '{"latitude":59, "longitude":18}'; Query OK, 0 rows affected (0.00 sec)  mysql> SET @point2 = '{"latitude":91, "longitude":0}'; Query OK, 0 rows affected (0.00 sec)  mysql> SET @point3 = '{"longitude":120}'; Query OK, 0 rows affected (0.00 sec)  mysql> INSERT INTO geo VALUES(@point1); Query OK, 1 row affected (0.05 sec)  mysql> INSERT INTO geo VALUES(@point2); ERROR 3819 (HY000): Check constraint 'geo_chk_1' is violated.  -- 查看原因 mysql> SHOW WARNINGS\G *************************** 1. row ***************************   Level: Error    Code: 3934 Message: The JSON document location '#/latitude' failed requirement 'maximum' at JSON Schema location '#/properties/latitude'. *************************** 2. row ***************************   Level: Error    Code: 3819 Message: Check constraint 'geo_chk_1' is violated. 2 rows in set (0.00 sec)  mysql> INSERT INTO geo VALUES(@point3); ERROR 3819 (HY000): Check constraint 'geo_chk_1' is violated. mysql> SHOW WARNINGS\G *************************** 1. row ***************************   Level: Error    Code: 3934 Message: The JSON document location '#' failed requirement 'required' at JSON Schema location '#'. *************************** 2. row ***************************   Level: Error    Code: 3819 Message: Check constraint 'geo_chk_1' is violated. 2 rows in set (0.00 sec) 3、JSON_SCHEMA_VALIDATION_REPORT()查看验证报告 语法:JSON_SCHEMA_VALIDATION_REPORT(schema,document) 该函数会以JSON文档的形式返回一个关于验证结果的报告。如果验证成功,返回{"valid": true}。如果JSON文档验证失败,该函数将返回一个JSON对象,该对象包含下面列出的属性: valid:false reason:失败原因 schema-location:校验失败的位置 document-location:失败位置 schema-failed-keyword:关键字或属性名  mysql> SET @schema = '{     '>  "id": "http://json-schema.org/geo",     '> "$schema": "http://json-schema.org/draft-04/schema#",     '> "description": "A geographical coordinate",     '> "type": "object",     '> "properties": {     '>   "latitude": {     '>     "type": "number",     '>     "minimum": -90,     '>     "maximum": 90     '>   },     '>   "longitude": {     '>     "type": "number",     '>     "minimum": -180,     '>     "maximum": 180     '>   }     '> },     '> "required": ["latitude", "longitude"]     '>}'; Query OK, 0 rows affected (0.01 sec)  mysql> SET @document = '{     '> "latitude": 63.444697,     '> "longitude": 10.445118     '>}'; Query OK, 0 rows affected (0.00 sec)  mysql> SELECT JSON_SCHEMA_VALIDATION_REPORT(@schema, @document); +---------------------------------------------------+ | JSON_SCHEMA_VALIDATION_REPORT(@schema, @document) | +---------------------------------------------------+ | {"valid": true}                                   | +---------------------------------------------------+ 1 row in set (0.00 sec) mysql> SET @document = '{     '> "latitude": 63.444697,     '> "longitude": 310.445118     '> }';  mysql> SELECT JSON_PRETTY(JSON_SCHEMA_VALIDATION_REPORT(@schema, @document))\G *************************** 1. row *************************** JSON_PRETTY(JSON_SCHEMA_VALIDATION_REPORT(@schema, @document)): {   "valid": false,   "reason": "The JSON document location '#/longitude' failed requirement 'maximum' at JSON Schema location '#/properties/longitude'",   "schema-location": "#/properties/longitude",   "document-location": "#/longitude",   "schema-failed-keyword": "maximum" } 1 row in set (0.00 sec)  mysql> SET @document = '{}'; Query OK, 0 rows affected (0.00 sec)  mysql> SELECT JSON_PRETTY(JSON_SCHEMA_VALIDATION_REPORT(@schema, @document))\G *************************** 1. row *************************** JSON_PRETTY(JSON_SCHEMA_VALIDATION_REPORT(@schema, @document)): {   "valid": false,   "reason": "The JSON document location '#' failed requirement 'required' at JSON Schema location '#'",   "schema-location": "#",   "document-location": "#",   "schema-failed-keyword": "required" } 1 row in set (0.00 sec)  mysql> SET @schema = '{     '> "id": "http://json-schema.org/geo",     '> "$schema": "http://json-schema.org/draft-04/schema#",     '> "description": "A geographical coordinate",     '> "type": "object",     '> "properties": {     '>   "latitude": {     '>     "type": "number",     '>     "minimum": -90,     '>     "maximum": 90     '>   },     '>   "longitude": {     '>     "type": "number",     '>     "minimum": -180,     '>     "maximum": 180     '>   }     '> }     '>}'; Query OK, 0 rows affected (0.00 sec)  mysql> SELECT JSON_SCHEMA_VALIDATION_REPORT(@schema, @document); +---------------------------------------------------+ | JSON_SCHEMA_VALIDATION_REPORT(@schema, @document) | +---------------------------------------------------+ | {"valid": true}                                   | +---------------------------------------------------+ 1 row in set (0.00 sec) 4、JSON_PRETTY()格式化输出 语法:JSON_PRETTY(json_val) 将 JSON 格式化输出。  SELECT JSON_PRETTY('123'); # scalar +--------------------+ | JSON_PRETTY('123') | +--------------------+ | 123                | +--------------------+  SELECT JSON_PRETTY("[1,3,5]"); # array +------------------------+ | JSON_PRETTY("[1,3,5]") | +------------------------+ | [   1,   3,   5 ]      | +------------------------+  SELECT JSON_PRETTY('{"a":"10","b":"15","x":"25"}'); # object +---------------------------------------------+ | JSON_PRETTY('{"a":"10","b":"15","x":"25"}') | +---------------------------------------------+ | {   "a": "10",   "b": "15",   "x": "25" }   | +---------------------------------------------+  SELECT JSON_PRETTY('["a",1,{"key1":     "value1"},"5",     "77" ,          {"key2":["value3","valueX",     "valueY"]},"j", "2"   ]')\G  # nested arrays and objects *************************** 1. row *************************** JSON_PRETTY('["a",1,{"key1":              "value1"},"5",     "77" ,                 {"key2":["value3","valuex",           "valuey"]},"j", "2"   ]'): [   "a",   1,   {     "key1": "value1"   },   "5",   "77",   {     "key2": [       "value3",       "valuex",       "valuey"     ]   },   "j",   "2" ] 5、JSON_STORAGE_FREE()计算空间 MySQL 8.0 新增的,与 Partial Updates 有关,用于计算 JSON 文档在进行部分更新后的剩余空间。  CREATE TABLE jtable (jcol JSON); INSERT INTO jtable VALUES ('{"a": 10, "b": "wxyz", "c": "[true, false]"}'); -- 更新,结果:{"a": 10, "b": "wxyz", "c": 1} UPDATE jtable SET jcol = JSON_SET(jcol, "$.a", 10, "$.b", "wxyz", "$.c", 1); -- 结果:14 SELECT JSON_STORAGE_FREE(jcol) FROM jtable;  -- 连续的部分更新对这个空闲空间的影响是累积的,如下例所示,使用JSON_SET()来减少具有键b的值所占用的空间(并且不做任何其他更改): UPDATE jtable SET jcol = JSON_SET(jcol, "$.a", 10, "$.b", "wx", "$.c", 1); -- 结果:16 SELECT JSON_STORAGE_FREE(jcol) FROM jtable;  -- 不使用JSON_SET()、JSON_REPLACE()或JSON_REMOVE()更新列意味着优化器不能就地执行更新;在这种情况下,JSON_STORAGE_FREE()返回0,如下所示: UPDATE jtable SET jcol = '{"a": 10, "b": 1}'; -- 结果:0 SELECT JSON_STORAGE_FREE(jcol) FROM jtable;  -- JSON文档的部分更新只能在列值上执行。对于存储JSON值的用户变量,该值总是被完全替换,即使使用JSON_SET()执行更新也是如此: SET @j = '{"a": 10, "b": "wxyz", "c": "[true, false]"}'; SET @j = JSON_SET(@j, '$.a', 10, '$.b', 'wxyz', '$.c', '1'); SELECT @j, JSON_STORAGE_FREE(@j) AS Free; -- 结果:0  -- 对于JSON文本,该函数总是返回0: SELECT JSON_STORAGE_FREE('{"a": 10, "b": "wxyz", "c": "1"}') AS Free; -- 结果:0  6、JSON_STORAGE_SIZE()计算空间 语法:JSON_STORAGE_SIZE(json_val) MySQL 5.7.22 引入的,用于计算 JSON 文档的空间使用情况。  CREATE TABLE jtable (jcol JSON); INSERT INTO jtable VALUES ('{"a": 1000, "b": "wxyz", "c": "[1, 3, 5, 7]"}'); SELECT jcol, JSON_STORAGE_SIZE(jcol) AS Size, JSON_STORAGE_FREE(jcol) AS Free FROM jtable; +-----------------------------------------------+------+------+ | jcol                                          | Size | Free | +-----------------------------------------------+------+------+ | {"a": 1000, "b": "wxyz", "c": "[1, 3, 5, 7]"} |   47 |    0 | +-----------------------------------------------+------+------+ 1 row in set (0.00 sec)  UPDATE jtable SET jcol = '{"a": 4.55, "b": "wxyz", "c": "[true, false]"}'; SELECT jcol, JSON_STORAGE_SIZE(jcol) AS Size, JSON_STORAGE_FREE(jcol) AS Free FROM jtable; +------------------------------------------------+------+------+ | jcol                                           | Size | Free | +------------------------------------------------+------+------+ | {"a": 4.55, "b": "wxyz", "c": "[true, false]"} |   56 |    0 | +------------------------------------------------+------+------+ 1 row in set (0.00 sec)  -- json文本显示占用存储空间 SELECT JSON_STORAGE_SIZE('[100, "sakila", [1, 3, 5], 425.05]') AS A,     JSON_STORAGE_SIZE('{"a": 1000, "b": "a", "c": "[1, 3, 5, 7]"}') AS B,      JSON_STORAGE_SIZE('{"a": 1000, "b": "wxyz", "c": "[1, 3, 5, 7]"}') AS C,      JSON_STORAGE_SIZE('[100, "json", [[10, 20, 30], 3, 5], 425.05]') AS D; +----+----+----+----+ | A  | B  | C  | D  | +----+----+----+----+ | 45 | 44 | 47 | 56 | +----+----+----+----+ 1 row in set (0.00 sec)  八、JSON字段创建索引 同 TEXT,BLOB 字段一样,JSON 字段不允许直接创建索引。 即使支持,实际意义也不大,因为我们一般是基于文档中的元素进行查询,很少会基于整个 JSON 文档。 对文档中的元素进行查询,就需要用到 MySQL 5.7 引入的虚拟列及函数索引。  # C2 即虚拟列 # index (c2) 对虚拟列添加索引。 create table t ( c1 json, c2 varchar(10) as (JSON_UNQUOTE(c1 -> "$.name")), index (c2) );  insert into t (c1) values  ('{"id": 1, "name": "a"}'), ('{"id": 2, "name": "b"}'), ('{"id": 3, "name": "c"}'), ('{"id": 4, "name": "d"}');  mysql> explain select * from t where c2 = 'a'; +----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key  | key_len | ref   | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------+ |  1 | SIMPLE      | t     | NULL       | ref  | c2            | c2   | 43      | const |    1 |   100.00 | NULL  | +----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------+ 1 row in set, 1 warning (0.00 sec)  mysql> explain select * from t where c1->'$.name' = 'a'; +----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key  | key_len | ref   | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------+ |  1 | SIMPLE      | t     | NULL       | ref  | c2            | c2   | 43      | const |    1 |   100.00 | NULL  | +----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------+ 1 row in set, 1 warning (0.00 sec) 可以看到,无论是使用虚拟列,还是文档中的元素来查询,都可以利用上索引。  注意,在创建虚拟列时需指定 JSON_UNQUOTE,将 c1 -> “$.name” 的返回值转换为字符串。  参考文档 https://dev.mysql.com/doc/refman/8.0/en/json.html https://blog.csdn.net/java_faep/article/details/125206014 https://zhuanlan.zhihu.com/p/514819634?utm_id=0 https://blog.csdn.net/sinat_20938225/article/details/129471550  GeoJSON:https://dev.mysql.com/doc/refman/8.0/en/spatial-geojson-functions.html json方法:https://dev.mysql.com/doc/refman/8.0/en/json-functions.html json索引:https://dev.mysql.com/doc/refman/8.0/en/create-table-secondary-indexes.html#json-column-indirect-index json多值索引:https://dev.mysql.com/doc/refman/8.0/en/create-index.html#create-index-multi-valued ————————————————                              版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。                          原文链接:https://blog.csdn.net/A_art_xiang/article/details/132472381 
  • [技术干货] VSCode打开Json文件格式化的简单步骤【转】
    VSCode打开Json文件格式化在VSCode中打开JSON文件时,你可以使用以下步骤来格式化JSON并显示为多行:使用快捷键:在打开的JSON文件中,使用快捷键格式化文档。Windows/Linux:Shift + Alt + FmacOS:Shift + Option + F右键菜单:在打开的JSON文件中,右键单击,选择 "Format Document" 或 "Format Selection"。命令面板:打开命令面板:Windows/Linux:Ctrl + Shift + PmacOS:Cmd + Shift + P输入 "Format Document" 并选择相应的命令。这些方法将自动格式化JSON文件,使其以更易读的多行形式显示。如果你只想格式化文件中的一部分,可以选择相应的文本,然后执行格式化命令。附:命令化格式化当JSON文件非常大时,建议使用该方法For Mac/Linux users:1cat ugly.json | python -mjson.tool > pretty.jsonFor Windows users1type ugly.json | python -mjson.tool > pretty.json
  • [迁移系列] 【PG迁移】移动json串累积求和
    示例drop table if exists tmp_ta; create temp table tmp_ta( gender char, type char, price text, number text, sequence int ); insert into tmp_ta values('M','O', 1,20, 1); insert into tmp_ta values('M','M', 2,40, 2); insert into tmp_ta values('F','O', 3,30, 3); insert into tmp_ta values('F','M', 4,50, 4); insert into tmp_ta values('M','M', 4,60, 5); insert into tmp_ta values('F','O',4,40, 6);获得效果gendertypepricenumberjsonb_stringsequenceFO330{"3": "30"}3FM450{"3": "30", "4": "50"}4FO440{"3": "30", "4": "40"}6MO120{"1": "20"}1MM240{"1": "20", "2": "40"}2MM460{"1": "20", "2": "40", "4": "60"}5采用SQL示例select gender, type, price, number ,jsonb_object_agg(price,number) over(partition by gender order by sequence),sequence from tmp_ta;对应效果说明jsonb_object_agg聚合函数配合over进行移动聚合,可以实现相关json的key去重、以及累加聚合;若需要实现机械json聚合(不根据key去重),可以采用jsonb_agg及jsonb_build_object拼接select gender, type, price, number ,jsonb_agg(jsonb_build_object(price,number)) over(partition by gender order by sequence),sequence from tmp_ta;
  • [技术干货] Jackson之多态反序列化【转】
    转自: https://zhuanlan.zhihu.com/p/96108902
  • [技术干货] 被项目吊打之我的踩坑日记 。
    这个月真的是“梦幻的一个月”,被项目吊打的欲仙欲s。有几件事情可能对各位大佬来说是常见的基础性问题,我就遇到这些问题抓耳挠腮。所以特地来记录下 。1.oracle超长字段的导出 。因为最近要频繁的导出数据,在某一天我就按部就班的导出数据,结果。。。这特么的是什么鬼啊 ,导出个数据就这么难么 .于是我化身某度,某歌 程序员 。疯狂输出 。我都用的是to_char怎么还是不能导出呢 ,后来搜到了 (DBMS_LOB.SUBSTR(substr(si.xxx, 1, 2000))),     然后我就按照这个导出,但是问题又来了 。。 明明我的字符没找过2000但是还是有问题,才想起来是不同编码字符集的中文占用的字节长度不同。 a. UTF-16字符集,一个汉字字符用2个或4个字节表示。    b. UTF-8变长字符集,所有字符用1~4个字节表示,一个汉字字符用3~4个字节表示。GBK字符集:汉字用2个字符表示。 贴出来做个记录 。之后就可以愉快的导出了 。2.distinct clob 字段 。因为要去重  就直接distinct了 ,之前一直没遇到过,没想到是不能直接distinct的 。oracle 10+g不支持对类型为CLOB的列进行distinct,也不支持union,所以在遇到此问题,需要对SQL语句进行重新,从另一思想去实现同样的效果的。union没仔细思考过,具体还要看union里面的条件如何,最简单的方法是利用to_char将clob字段转换成char型,但存在一个问题,如clob中的字符长度超过4000时会报错。3.redis冲突自己搭建了2个项目  但是为了省事  就用的是一套数据库环境  当然也包括了 redis。  结果发现启动后,项目里面就报错,疯狂查找不到原因,人都懵了  。后来在大佬的指点下才发现 2个redis公用的0号库 。。。这就会导致redis冲突 。(项目经验过少的锅,我不背锅。)4.oracle游标的使用 。这个事还是因为1的原因引起的吧 。 因为部分时候 需要批量入一些数据,或者更新一些数据 。一个个对的 我就好累  。为了偷懒就想办法(所以懒是第一生产力么 ?)DECLARE cursor cur is--定义一个游标 select ID, VALUESS from UPDATE; BEGIN FOR x IN cur LOOP --循环游标 UPDATE d SET d.= replace(d.jsonContent, 'xxx' ,x.VALUESS) where d.id = x.id; end loop; end;对了 上面顺便也写了 处理json数据的办法。
  • [技术干货] 使用 Fastai 构建食物图像分类器【转载】
    背景社交媒体平台是分享有趣的图像的常用方式。食物图像,尤其是与不同的美食和文化相关的图像,是一个似乎经常流行的话题。Instagram 等社交媒体平台拥有大量属于不同类别的图像。我们都可能使用谷歌图片或 Instagram 上的搜索选项来浏览看起来很美味的蛋糕图片来寻找灵感。但是为了让这些图片可以通过搜索获得,我们需要为每张图片设置一些相关的标签。这使得搜索关键字并将其与标签匹配成为可能。由于手动标记每张图像极具挑战性,因此公司使用 ML (机器学习)和 DL (深度学习)技术为图像生成正确的标签。这可以使用基于一些标记数据识别和标记图像的图像分类器来实现。在本文中,让我们使用 fastai 构建一个图像分类器,并使用一个名为“ fastai”的库来识别一些食物图像。Fastai 简介Fastai 是一个开源深度学习库,它为从业者提供高级组件,可以快速轻松地在传统深度学习领域产生最先进的结果。它使研究人员可以混合和组合低级组件以创建新技术。它旨在在不影响可用性、灵活性或性能的情况下实现这两个目标。由于 fastai 是用 Python 编写的,并且基于 PyTorch,因此需要 Python 知识才能理解本文。我们将在 Google Colab 中运行此代码。除了 fastai,我们将使用图形处理单元 (GPU) 以尽可能快地获得结果。使用 Fastai 构建图像分类器让我们从安装 fastai 库开始:!pip install -Uqq fastai如果你使用的是 Anaconda,请运行以下命令:conda install -c fastchan fastai anaconda让我们导入分类任务所需的包。该库分为模块,其中最常见的是表格、文本和视觉。因为我们手头的任务包括视觉,所以我们从vision库中导入我们需要的所有功能。from fastai.vision.all import *通过 fastai 库可以获得许多学术数据集。其中之一是 FOOD,它是 URL 下的URLs. FOOD第一步是获取并提取我们需要的数据。我们将使用 untar_data 函数,它会自动下载数据集并解压它。foodPath = untar_data(URLs.FOOD)该数据集包含 101,000 张图像,分为 101 个食物类别,每个类别有 250 个测试图像和 750 个训练图像。训练中的图像没有被清理。所有图像的大小都调整为每边最大 512 像素。下一个命令将告诉我们必须处理多少图像。len(get_image_files(foodPath))此外,使用以下命令,我们将打印 Food 数据集的元目录的内容。print(os.listdir(foodPath))meta文件夹包含八个文件,其中四个是文本文件:train.txt、test.txt、classes.txt和labels.txt。train.txt 和 test.txt 文件分别包含训练集和测试集的图像列表。classes.txt 文件包含所有食品类别和标签的列表。txt 提供了所有食品图像标签的列表。该目录还包含一个带有预训练模型的 .h5 文件和一个包含 101,000 张 JPG 格式图像的图像文件夹。最后,训练集和测试集以 JSON 格式提供。要查看所有图像类别,我们将运行以下命令:image_dir_path = foodPath/'images'image_categories = os.listdir(image_dir_path)print(image_categories)然后,我们将执行以下命令以查看 101,000 张图像集合中的示例图像。img = PILImage.create('/root/.fastai/data/food-101/images/frozen_yogurt/1942235.jpg')img.show();我们将使用 pandas 函数读取 JSON 格式的训练和测试文件。JSON 是一种以人类可读的形式存储信息的数据格式。以下代码从目录中读取 train.json 文件并将结果保存在 df_train 数据帧中。df_train=pd.read_json('/root/.fastai/data/food-101/train.json')然后可以使用 head() 函数打印数据帧的标题,如下所示。df_train.head()同样,通过使用 pandas 函数,我们将读取 test.json 文件并将其存储在 df_test 数据帧中。df_test=pd.read_json('/root/.fastai/data/food-101/test.json')df_test.head()我们正在创建三个带有我们选择的食物名称的标签来对食物图像进行分类。labelA = 'cheesecake'labelB = 'donuts'labelC= 'panna_cotta'现在我们将创建一个 for 循环,它将遍历我们下载的所有图像。在此循环的帮助下,我们将删除没有标签 A、B 或 C 的图像。此外,我们使用以下函数重命名具有各自标签的图像。for img in get_image_files(foodPath): if labelA in str(img):  img.rename(f"{img.parent}/{labelA}-{img.name}") elif labelB in str(img):   img.rename(f"{img.parent}/{labelB}-{img.name}") elif labelC in str(img):   img.rename(f"{img.parent}/{labelC}-{img.name}") else: os.remove(img)让我们使用以下命令检查运行循环后获得的图像数量:len(get_image_files(foodPath))让我们在三个选择的食物中尝试一个示例标签,看看重命名是否正确。def GetLabel(fileName):return fileName.split('-')[0]GetLabel("cheesecake-1092082.jpg")以下代码生成一个 DataLoaders 对象,该对象表示训练和验证数据的混合。dls = ImageDataLoaders.from_name_func(   foodPath, get_image_files(foodPath), valid_pct=0.2, seed=42,   label_func=GetLabel, item_tfms=Resize(224))dls.train.show_batch()在这种情况下,我们将:· 使用路径选项指定下载和提取数据的位置。· 使用 get_image_ files 函数从指定位置收集所有文件名。· 对数据集使用 80–20 拆分。· 使用 GetLabel 函数从文件名中提取标签。· 将所有图像调整为相同大小,即 224 像素。· 使用 show_batch 函数生成一个输出窗口,显示带有指定标签的训练图像网格。是时候将模型放置到位了。使用 ResNet34 架构,我们将通过专注于称为 vision_learner () 的单个函数调用来构建卷积神经网络。vision_learner 函数(也称为 cnn_learner)有利于训练计算机视觉模型。它包括你的原始图像数据集、预训练模型 resnet34 和一个度量错误率,它决定了在验证数据中错误识别的图像的比例。resnet34 中的 34 指的是这种架构类型中的层数(其他选项有 18、50、101 和 152)。使用更多层的模型需要更长的训练时间并且更容易过度拟合。Fastai 提供了一个“fine_tune”函数,用于调整预训练模型,以使用我们选择的数据解决我们的特定问题。为了训练模型,我们将 epoch 数设置为 10。learn = vision_learner(dls, resnet34, metrics=error_rate, pretrained=True)learn.fine_tune(epochs=10)也可以通过将指标替换为“accuracy”来检查相同模型的准确性。从上面的结果,我们可以说,即使只有 10 个 epoch,预训练的 ResNet34 模型在多标签分类任务中表现出 > 85% 的良好准确率。如果我们增加 epoch 的数量,模型的准确性可能会提高。现在,让我们测试一些示例图像来检查我们的模型的性能。示例图片 #1示例图片 #2示例图片 #3从上面的结果,我们可以说我们的模型能够正确识别样本图像。训练模型后,我们可以将其部署为 Web 应用程序供其他人使用。尽管 fastai 主要用于模型训练,但你可以使用“learn.export”函数快速导出 PyTorch 模型以用于生产。结论在本教程中,我们学习了如何使用基于 PyTorch 的 fastai 构建食物图像分类器。可以使用 Heroku 或 Netlify 等服务部署此模型,以使此模型可用作 Web 应用程序。以下是本文的一些主要内容:我们可以使用 fastai 以最少的代码建立深度学习模型。因此,fastai 使得使用 PyTorch 进行深度学习任务变得更加容易。食品分类对于计算机视觉应用来说是一项具有挑战性的任务,因为根据装饰和供应方式的不同,同一种食品在不同地方看起来可能会有很大差异。尽管如此,通过利用迁移学习的力量,我们可以使用预训练模型来识别食品并对其进行正确分类。我们为此分类器使用了预训练模型 ResNet34。但是,你可以使用其他预训练模型,如 VGG、Inception、DenseNet 等,来构建你自己的模型。转载自https://iot.ofweek.com/2022-08/ART-132200-11000-30570049.html
  • [技术干货] 事件中心学习之——阿里云
    事件中心将云产品所生成的事件数据进行统一管理、存储、分析和展示,已接入EDAS的变更事件、ARMS的报警事件、0-1事件(如死锁、OOM和应用启动等)、MSE的微服务管控事件和K8s集群事件。当您的应用使用了相关的产品,对应的事件会自动接入事件中心进行统一的分析展示,方便您查看与分析。事件模型事件中心的一个事件主要由以下参数来定义:参数是否必须描述source是事件来源type是事件类型level是事件等级time是事件发生时间data是事件体(一般为JSON格式)PID否Pod IDIP否IP地址ClusterId否集群IDPodName否Pod名称进入事件中心登录ARMS控制台。在左侧导航栏选择应用监控 > 事件中心,并在顶部菜单栏选择目标地域。事件中心模块介绍事件中心主要包含典型事件和四个页签(分别是普通视图,拓扑视图、集群视图和订阅规则)。典型事件:展示系统预置的典型事件的数量。更多信息,请参见典型事件。普通视图将与当前应用关联的所有事件进行简单的多维度分析与展示。更多信息,请参见普通视图。拓扑视图将当前应用关联的事件和与当前应用的资源拓扑图进行结合展示。更多信息。请参见拓扑视图。集群视图将K8s集群关联的所有事件进行简单的多维度分析与展示。更多信息。请参见集群视图。订阅规则以列表形式展示了当前您创建的订阅规则。更多信息,请参见订阅规则。典型事件典型事件:展示系统预置的典型事件类型在最近30分钟(可在右上角调整时间范围)内发生的次数。在事件下方单击订阅,可以编辑该事件的订阅规则。订阅规则的操作,请参见订阅规则。普通视图普通视图可以按照您指定的检索条件进行搜索,搜索的结果会以四个视图进行展示,分别是:最近两周事件热力图、不同来源事件占比、不同来源事件数量走势和事件详情。最近两周事件热力图展示近两周内满足过滤条件的事件发生次数按小时统计的热力分布。颜色越深,说明该小时内发生的事件数量越多。不同来源事件占比展示不同来源事件的数量占比。不同来源事件数量走势展示不同来源事件在选定时段内的走势。事件详情展示当前所有事件的详情列表。普通视图查看说明:单击最近两周事件热力图中的热力方块,可查看该小时内的事件详情。单击不同来源事件数量走势中的立柱,可在事件详情区域查看对应时段内的所有事件列表。单击事件详情右侧的查看,可以查看事件的具体内容。单击事件详情右侧的订阅,可以订阅指定事件。更多信息,请参见订阅规则。拓扑视图拓扑视图首先会绘制出该应用的资源拓扑,包含该应用使用的ECS,该应用部署的实例Pod,该应用使用的所有RDS和Redis等中间件资源,以及该应用挂载的SLB和NAT等。之后会将获取到的关联事件、操作审计、云监控事件关联到对应的拓扑节点上,单击相应节点,会在左上角的事件详情区域展示与该节点关联的事件:应用侧相关事件:即事件中心的事件。云资源侧相关事件:即云监控的相关事件。相关操作审计:即来自于操作审计的审计记录。该视图可以帮助您在应用出现故障时,快速排查关联的资源各自发生了什么问题。例如在大型企业中,由于某个员工的误操作,重启了生产环境的RDS,导致线上业务故障,利用该视图,可以快速的发现应用访问的RDS出现了重启操作。集群视图集群视图可以按照您指定的检索条件进行搜索,搜索的结果会以四个视图进行展示,分别是:不同集群事件数量占比、不同集群事件数量走势、关键事件一览、关键资源一览和事件详情。不同集群事件数量占比:展示不同集群事件的数量占比。不同集群事件数量走势:展示不同集群事件在选定时段内的走势。关键事件一览:展示指定集群下系统预置的典型事件类型在一段时间内发生的次数。关键资源一览:展示指定集群下关键资源的详情列表。事件详情:展示指定集群下所有事件的详情列表。集群视图查看说明:单击不同集群事件数量走势中的立柱,可在事件详情区域查看对应时段内的所有事件列表。单击查看集群相关事件,可在普通视图页签查看所有集群相关事件详情。单击关键事件一览中的事件名称,可在普通视图页签查看对应事件详情。单击关键资源一览中的查看生命周期相关事件,可在事件详情区域查看对应时段内的资源对应的事件列表。单击事件详情右侧的查看,可以查看事件的具体内容。单击事件详情右侧的订阅,可以订阅指定事件。更多信息,请参见订阅规则。订阅规则订阅规则页签展示您当前所有的订阅规则,您可以在该页面启用、禁用或者修改规则。订阅规则是一条表示您订阅满足指定条件的事件,并将该事件发送到指定Webhook的规则。创建订阅规则有两种方式:方式一:在订阅规则页签右上角单击新建订阅规则。方式二:在普通视图页签的事件列表区域单击事件的订阅。新建订阅规则单击订阅规则页签,在页面右上角单击新建订阅规则。在创建订阅规则面板的填写基本信息页面输入规则名称和规则描述,然后单击下一步。在选择事件模式页面设置事件规则参数,然后单击下一步。参数描述事件来源在下拉列表中选择事件来源。事件类型在下拉列表中选择事件类型。事件等级在下拉列表中选择事件等级。事件关键字在文本框中输入事件关键字。显示高级过滤选项默认关闭,开启后可设置关联应用ID、主机IP、集群ID和POD名过滤选项。自定义过滤条件自定义过滤条件一般用来指定事件体JSON的某个字段需要满足的条件,根节点是data,以.的形式下钻事件体JSON的某个字段。请输入自定义的过滤条件,最多可以设置6条过滤条件。选择有效字段以%data.x.y;的形式选择有效字段,选择完成后,请给选择的字段输入一个别名,该别名可用于在填写Webhook信息的Post请求体中以占位符的形式出现。最多可以设置6条有效字段。消息通知模板消息通知模板会作为当指定消息发生时通知给您的内容。如果通知对象为钉钉机器人Webhook,请注意在消息模板中包含创建钉钉机器人Webhook时的关键字。在选择联系人页面选择联系人,然后单击提交。如果选择联系人的列表中没有联系人信息,在右侧单击新建联系人,创建联系人后再在列表中选择。在普通视图中订阅事件在普通视图页签的事件详情区域,单击事件详情右侧的订阅。以该种方式创建的订阅规则,会根据您选择的事件来自动选择事件来源、事件类型和事件等级三个过滤条件。单击事件JSON文件中某个字段Value的形式来选择自定义过滤条件和有效字段。每次单击会自动生成一个过滤条件和选择字段,同时您还可以手动修改或者删除字段来调整规则,完成事件模式设置后单击下一步。在选择联系人页面选择联系人,然后单击提交。如果选择联系人的列表中没有联系人信息,在右侧单击新建联系人,创建联系人后再在列表中选择。
  • [技术干货] Python实现yaml与json文件批量互转【转】
    目录1. 安装yaml库2. yaml转json3. json转yaml4. 批量将yaml与json文件互相转换1. 安装yaml库想要使用python实现yaml与json格式互相转换,需要先下载pip,再通过pip安装yaml库。如何下载以及使用pip,可参考:pip的安装与使用,解决pip下载速度慢的问题安装yaml库:1pip install pyyaml2. yaml转json新建一个test.yaml文件,添加以下内容:123456789A:     hello:          name: Michael              address: Beijing            B:     hello:          name: jack           address: Shanghai代码如下:1234567891011121314import yamlimport json # yaml文件内容转换成json格式def yaml_to_json(yamlPath):    with open(yamlPath, encoding="utf-8") as f:        datas = yaml.load(f,Loader=yaml.FullLoader)  # 将文件的内容转换为字典形式    jsonDatas = json.dumps(datas, indent=5) # 将字典的内容转换为json格式的字符串    print(jsonDatas) if __name__ == "__main__":    jsonPath = 'E:/Code/Python/test/test.yaml'    yaml_to_json(jsonPath)    执行结果如下:{     "A": {          "hello": {               "name": "Michael",                  "address": "Beijing"           }     },     "B": {          "hello": {               "name": "jack",                     "address": "Shanghai"          }     }}3. json转yaml新建一个test.json文件,添加以下内容:1234567891011121314{    "A": {         "hello": {            "name": "Michael",            "address": "Beijing"                   }    },    "B": {         "hello": {            "name": "jack",            "address": "Shanghai"            }    }}代码如下:12345678910111213import yamlimport json # json文件内容转换成yaml格式def json_to_yaml(jsonPath):    with open(jsonPath, encoding="utf-8") as f:        datas = json.load(f) # 将文件的内容转换为字典形式    yamlDatas = yaml.dump(datas, indent=5, sort_keys=False) # 将字典的内容转换为yaml格式的字符串    print(yamlDatas) if __name__ == "__main__":    jsonPath = 'E:/Code/Python/test/test.json'    json_to_yaml(jsonPath)执行结果如下:A:     hello:          name: Michael          address: BeijingB:     hello:          name: jack          address: Shanghai注意,如果不加sort_keys=False,那么默认是排序的,则执行结果如下:A:     hello:          address: Beijing          name: MichaelB:     hello:          address: Shanghai          name: jack4. 批量将yaml与json文件互相转换yaml与json文件互相转换:123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132import yamlimport jsonimport osfrom pathlib import Pathfrom fnmatch import fnmatchcase class Yaml_Interconversion_Json:    def __init__(self):        self.filePathList = []         # yaml文件内容转换成json格式    def yaml_to_json(self, yamlPath):        with open(yamlPath, encoding="utf-8") as f:            datas = yaml.load(f,Loader=yaml.FullLoader)          jsonDatas = json.dumps(datas, indent=5)        # print(jsonDatas)        return jsonDatas     # json文件内容转换成yaml格式    def json_to_yaml(self, jsonPath):        with open(jsonPath, encoding="utf-8") as f:            datas = json.load(f)        yamlDatas = yaml.dump(datas, indent=5)        # print(yamlDatas)        return yamlDatas     # 生成文件    def generate_file(self, filePath, datas):        if os.path.exists(filePath):            os.remove(filePath)         with open(filePath,'w') as f:            f.write(datas)     # 清空列表    def clear_list(self):        self.filePathList.clear()     # 修改文件后缀    def modify_file_suffix(self, filePath, suffix):        dirPath = os.path.dirname(filePath)        fileName = Path(filePath).stem + suffix        newPath = dirPath + '/' + fileName        # print('{}_path:{}'.format(suffix, newPath))        return newPath     # 原yaml文件同级目录下,生成json文件    def generate_json_file(self, yamlPath, suffix ='.json'):        jsonDatas = self.yaml_to_json(yamlPath)        jsonPath = self.modify_file_suffix(yamlPath, suffix)        # print('jsonPath:{}'.format(jsonPath))        self.generate_file(jsonPath, jsonDatas)     # 原json文件同级目录下,生成yaml文件    def generate_yaml_file(self, jsonPath, suffix ='.yaml'):        yamlDatas = self.json_to_yaml(jsonPath)        yamlPath = self.modify_file_suffix(jsonPath, suffix)        # print('yamlPath:{}'.format(yamlPath))        self.generate_file(yamlPath, yamlDatas)     # 查找指定文件夹下所有相同名称的文件    def search_file(self, dirPath, fileName):        dirs = os.listdir(dirPath)         for currentFile in dirs:             absPath = dirPath + '/' + currentFile             if os.path.isdir(absPath):                 self.search_file(absPath, fileName)            elif currentFile == fileName:                self.filePathList.append(absPath)     # 查找指定文件夹下所有相同后缀名的文件    def search_file_suffix(self, dirPath, suffix):        dirs = os.listdir(dirPath)         for currentFile in dirs:             absPath = dirPath + '/' + currentFile             if os.path.isdir(absPath):                if fnmatchcase(currentFile,'.*'):                     pass                else:                    self.search_file_suffix(absPath, suffix)            elif currentFile.split('.')[-1] == suffix:                 self.filePathList.append(absPath)     # 批量删除指定文件夹下所有相同名称的文件    def batch_remove_file(self, dirPath, fileName):        self.search_file(dirPath, fileName)        print('The following files are deleted:{}'.format(self.filePathList))        for filePath in self.filePathList:            if os.path.exists(filePath):                os.remove(filePath)         self.clear_list()     # 批量删除指定文件夹下所有相同后缀名的文件    def batch_remove_file_suffix(self, dirPath, suffix):        self.search_file_suffix(dirPath, suffix)        print('The following files are deleted:{}'.format(self.filePathList))        for filePath in self.filePathList:            if os.path.exists(filePath):                os.remove(filePath)         self.clear_list()     # 批量将目录下的yaml文件转换成json文件    def batch_yaml_to_json(self, dirPath):        self.search_file_suffix(dirPath, 'yaml')        print('The converted yaml file is as follows:{}'.format(self.filePathList))        for yamPath in self.filePathList:            try:                self.generate_json_file(yamPath)            except Exception as e:                print('YAML parsing error:{}'.format(e))                 self.clear_list()     # 批量将目录下的json文件转换成yaml文件    def batch_json_to_yaml(self, dirPath):        self.search_file_suffix(dirPath, 'json')        print('The converted json file is as follows:{}'.format(self.filePathList))        for jsonPath in self.filePathList:            try:                self.generate_yaml_file(jsonPath)            except Exception as e:                print('JSON parsing error:{}'.format(jsonPath))                print(e)        self.clear_list() if __name__ == "__main__":    dirPath = 'C:/Users/hwx1109527/Desktop/yaml_to_json'    fileName = 'os_deploy_config.yaml'    suffix = 'yaml'    filePath = dirPath + '/' + fileName    yaml_interconversion_json = Yaml_Interconversion_Json()    yaml_interconversion_json.batch_yaml_to_json(dirPath)    # yaml_interconversion_json.batch_json_to_yaml(dirPath)    # yaml_interconversion_json.batch_remove_file_suffix(dirPath, suffix)
  • [技术干货] @ResponseBody注解的总结
    1.@ResponseBody 注解的作用      @ResponseBody 是spring中的注解,注解没有任何参数,@ResponseBody 注解的主要作用是将Controller的方法返回的对象,通过转换器转换为指定的格式之后,写入到HTTP response body中,通常用来返回JSON数据或者是xml数据。2.@ResponseBody使用在哪里      @ResponseBody注解一般会使用在Controller的方法上;也可以使用在整个Controller上,代表整个Controller中的方法都会采用@ResponseBody的方式转换数据,如果作用在整个Controller上可以和@Controller注解合并成@RestController注解来使用。      @ResponseBody注解还会起到ajax请求结束标识符的功能,当ajax请求从流中拿到这个注解才会结束请求,继续向下进行程序的运行.3.@ResponseBody注解的浅析     在方法上使用了@RequestMapping注解的时候,方法的返回值通常解析为跳转的路径,即需要跳转到指定的页面。当添加了 @ResponseBody 这个注解后, 则表明该方法的返回值直接写入到 HTTP Response Body 中。@ResponseBody可以标注任何对象,由Srping完成对象和协议的转换。     如果是入参是字符串则直接将字符串写到客户端,如果入参是是一个对象,此时会将对象转化为json字符串然后再写到客户端。这里需要注意的是,如果返回对象,按utf-8编码。如果返回String,默认按iso8859-1编码,页面可能出现乱码。因此在注解中我们可以手动修改编码格式,例如@RequestMapping(value="/cat/query",produces="text/html;charset=utf-8"),前面是请求的路径,后面是编码格式。      @ResponseBody 注解中让Controller控制层方法的返回值转化为json格式的字符串是通过HttpMessageConverter中的方法实现类的转换的。如果是入参是bean对象,会调用对象的getXXX()方法获取属性值并且以键值对的形式进行封装,进而转化为json字符串。如果入参是是map集合,则会采用get(key)方式获取value值,然后进行封装,然后封装到形参上。      以上就是@ResponseBody 注解的相关知识的总结,继续努力,加油~~~
  • [基础知识] MindSpore易点通·精讲系列--数据集加载之TFRecordDataset
    # Dive Into MindSpore -- TFRecordDataset For Dataset Load > MindSpore易点通·精讲系列--数据集加载之TFRecordDataset 本文开发环境 - Ubuntu 20.04 - Python 3.8 - MindSpore 1.7.0 本文内容摘要 - 背景介绍 - 先看文档 - 生成TFRecord - 数据加载 - 本文总结 - 本文参考 ## 1. 背景介绍 `TFRecord`格式是`TensorFlow`官方设计的一种数据格式。 `TFRecord` 格式是一种用于存储二进制记录序列的简单格式,该格式能够更好的利用内存,内部包含多个`tf.train.Example`,在一个`Examples`消息体中包含一系列的`tf.train.feature`属性,而每一个`feature`是一个`key-value`的键值对,其中`key`是string类型,`value`的取值有三种: - bytes_list:可以存储`string`和`byte`两种数据类型 - float_list:可以存储`float(float32)`和`double(float64)`两种数据类型 - int64_list:可以存储`bool, enum, int32, uint32, int64, uint64`数据类型 上面简单介绍了`TFRecord`的知识,下面我们就要进入正题,来谈谈`MindSpore`中对`TFRecord`格式的支持。 ## 2. 先看文档 老传统,先来看看官方对API的描述。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/11/1657534352422292150.png) 下面对主要参数做简单介绍: - dataset_files -- 数据集文件路径。 - schema -- 读取模式策略,通俗来说就是要读取的tfrecord文件内的数据内容格式。可以通过json或者Schema传入。默认为None不指定。 - columns_list -- 指定读取的具体数据列。默认全部读取。 - num_samples -- 指定读取出来的样本数量。 - shuffle -- 是否对数据进行打乱,可参考之前的文章解读。 ## 3. 生成TFRecord > 本文使用的是`THUCNews`数据集,如果需要将该数据集用于商业用途,请联系[数据集作者](http://thuctc.thunlp.org/)。 > > [数据集启智社区下载地址](https://git.openi.org.cn/kaierlong/THUCNews-Code/datasets) 由于下文需要用到`TFRecord`数据集来做加载,本节先来生成`TFRecord`数据集。对`TensorFlow`不了解的读者可以直接照搬代码即可。 生成`TFRecord`代码如下: ```python import codecs import os import re import six import tensorflow as tf from collections import Counter def _int64_feature(values): """Returns a TF-Feature of int64s. Args: values: A scalar or list of values. Returns: A TF-Feature. """ if not isinstance(values, (tuple, list)): values = [values] return tf.train.Feature(int64_list=tf.train.Int64List(value=values)) def _float32_feature(values): """Returns a TF-Feature of float32s. Args: values: A scalar or list of values. Returns: A TF-Feature. """ if not isinstance(values, (tuple, list)): values = [values] return tf.train.Feature(float_list=tf.train.FloatList(value=values)) def _bytes_feature(values): """Returns a TF-Feature of bytes. Args: values: A scalar or list of values. Returns: A TF-Feature """ if not isinstance(values, (tuple, list)): values = [values] return tf.train.Feature(bytes_list=tf.train.BytesList(value=values)) def convert_to_feature(values): """Convert to TF-Feature based on the type of element in values. Args: values: A scalar or list of values. Returns: A TF-Feature. """ if not isinstance(values, (tuple, list)): values = [values] if isinstance(values[0], int): return _int64_feature(values) elif isinstance(values[0], float): return _float32_feature(values) elif isinstance(values[0], bytes): return _bytes_feature(values) else: raise ValueError("feature type {0} is not supported now !".format(type(values[0]))) def dict_to_example(dictionary): """Converts a dictionary of string->int to a tf.Example.""" features = {} for k, v in six.iteritems(dictionary): features[k] = convert_to_feature(values=v) return tf.train.Example(features=tf.train.Features(feature=features)) def get_txt_files(data_dir): cls_txt_dict = {} txt_file_list = [] # get files list and class files list. sub_data_name_list = next(os.walk(data_dir))[1] sub_data_name_list = sorted(sub_data_name_list) for sub_data_name in sub_data_name_list: sub_data_dir = os.path.join(data_dir, sub_data_name) data_name_list = next(os.walk(sub_data_dir))[2] data_file_list = [os.path.join(sub_data_dir, data_name) for data_name in data_name_list] cls_txt_dict[sub_data_name] = data_file_list txt_file_list.extend(data_file_list) num_data_files = len(data_file_list) print("{}: {}".format(sub_data_name, num_data_files), flush=True) num_txt_files = len(txt_file_list) print("total: {}".format(num_txt_files), flush=True) return cls_txt_dict, txt_file_list def get_txt_data(txt_file): with codecs.open(txt_file, "r", "UTF8") as fp: txt_content = fp.read() txt_data = re.sub("\s+", " ", txt_content) return txt_data def build_vocab(txt_file_list, vocab_size=7000): counter = Counter() for txt_file in txt_file_list: txt_data = get_txt_data(txt_file) counter.update(txt_data) num_vocab = len(counter) if num_vocab vocab_size - 1: real_vocab_size = num_vocab + 2 else: real_vocab_size = vocab_size # pad_id is 0, unk_id is 1 vocab_dict = {word_freq[0]: ix + 1 for ix, word_freq in enumerate(counter.most_common(real_vocab_size - 2))} print("real vocab size: {}".format(real_vocab_size), flush=True) print("vocab dict:\n{}".format(vocab_dict), flush=True) return vocab_dict def make_tfrecords( data_dir, tfrecord_dir, vocab_size=7000, min_seq_length=10, max_seq_length=800, num_train=8, num_test=2, start_fid=0): # get txt files cls_txt_dict, txt_file_list = get_txt_files(data_dir=data_dir) # map word to id vocab_dict = build_vocab(txt_file_list=txt_file_list, vocab_size=vocab_size) # map class to id class_dict = {class_name: ix for ix, class_name in enumerate(cls_txt_dict.keys())} train_writers = [] for fid in range(start_fid, num_train+start_fid): tfrecord_file = os.path.join(tfrecord_dir, "train_{:04d}.tfrecord".format(fid)) writer = tf.io.TFRecordWriter(tfrecord_file) train_writers.append(writer) test_writers = [] for fid in range(start_fid, num_test+start_fid): tfrecord_file = os.path.join(tfrecord_dir, "test_{:04d}.tfrecord".format(fid)) writer = tf.io.TFRecordWriter(tfrecord_file) test_writers.append(writer) pad_id = 0 unk_id = 1 num_samples = 0 num_train_samples = 0 num_test_samples = 0 for class_name, class_file_list in cls_txt_dict.items(): class_id = class_dict[class_name] num_class_pass = 0 for txt_file in class_file_list: txt_data = get_txt_data(txt_file=txt_file) txt_len = len(txt_data) if txt_len min_seq_length: num_class_pass += 1 continue if txt_len > max_seq_length: txt_data = txt_data[:max_seq_length] txt_len = max_seq_length word_ids = [] for word in txt_data: word_id = vocab_dict.get(word, unk_id) word_ids.append(word_id) for _ in range(max_seq_length - txt_len): word_ids.append(pad_id) example = dict_to_example({"input": word_ids, "class": class_id}) num_samples += 1 if num_samples % 10 == 0: num_test_samples += 1 writer_id = num_test_samples % num_test test_writers[writer_id].write(example.SerializeToString()) else: num_train_samples += 1 writer_id = num_train_samples % num_train train_writers[writer_id].write(example.SerializeToString()) print("{} pass: {}".format(class_name, num_class_pass), flush=True) for writer in train_writers: writer.close() for writer in test_writers: writer.close() print("num samples: {}".format(num_samples), flush=True) print("num train samples: {}".format(num_train_samples), flush=True) print("num test samples: {}".format(num_test_samples), flush=True) def main(): data_dir = "{your_data_dir}" tfrecord_dir = "{your_tfrecord_dir}" make_tfrecords(data_dir=data_dir, tfrecord_dir=tfrecord_dir) if __name__ == "__main__": main() ``` 将以上代码保存到文件`make_tfrecord.py`,运行命令: > 注意:需要替换`data_dir`和`tfrecord_dir`为个人目录。 ```shell python3 make_tfrecord.py ``` 使用`tree`命令查看生成的`TFRecord`数据目录,输出内容如下: ```shell . ├── test_0000.tfrecord ├── test_0001.tfrecord ├── train_0000.tfrecord ├── train_0001.tfrecord ├── train_0002.tfrecord ├── train_0003.tfrecord ├── train_0004.tfrecord ├── train_0005.tfrecord ├── train_0006.tfrecord └── train_0007.tfrecord 0 directories, 10 files ``` ## 4. 数据加载 有了`3`中的`TFRecord`数据集,下面来介绍如何在`MindSpore`中使用该数据集。 ### 4.1 schema使用 #### 4.1.1 不指定schema 首先来看看对于参数`schema`不指定,即采用默认值的情况下,能否正确读取数据。 代码如下: ```python import os from mindspore.common import dtype as mstype from mindspore.dataset import Schema from mindspore.dataset import TFRecordDataset def get_tfrecord_files(tfrecord_dir, file_suffix="tfrecord", is_train=True): if not os.path.exists(tfrecord_dir): raise ValueError("tfrecord directory: {} not exists!".format(tfrecord_dir)) if is_train: file_prefix = "train" else: file_prefix = "test" data_sources = [] for parent, _, filenames in os.walk(tfrecord_dir): for filename in filenames: if not filename.startswith(file_prefix): continue tmp_path = os.path.join(parent, filename) if tmp_path.endswith(file_suffix): data_sources.append(tmp_path) return data_sources def load_tfrecord(tfrecord_dir, tfrecord_json=None): tfrecord_files = get_tfrecord_files(tfrecord_dir) # print("tfrecord files:\n{}".format("\n".join(tfrecord_files)), flush=True) dataset = TFRecordDataset(dataset_files=tfrecord_files, shuffle=False) data_iter = dataset.create_dict_iterator() for item in data_iter: print(item, flush=True) break def main(): tfrecord_dir = "{your_tfrecord_dir}" tfrecord_json = "{your_tfrecord_json_file}" load_tfrecord(tfrecord_dir=tfrecord_dir, tfrecord_json=None) if __name__ == "__main__": main() ``` 代码解读: - get_tfrecord_files -- 获取指定的TFRecord文件列表 - load_tfrecord -- 数据集加载 将上述代码保存到文件`load_tfrecord_dataset.py`,运行如下命令: ```shell python3 load_tfrecord_dataset.py ``` 输出内容如下: > 可以看出能正确解析出之前保存在TFRecord内的数据,数据类型和数据维度解析正确。 ```shell {'class': Tensor(shape=[1], dtype=Int64, value= [0]), 'input': Tensor(shape=[800], dtype=Int64, value= [1719, 636, 1063, 18, ...... 135, 979, 1, 35, 166, 181, 90, 143])} ``` #### 4.1.2 使用Schema对象 下面介绍,如何使用`mindspore.dataset.Schema`来指定读取模型策略。 修改`load_tfrecord`代码如下: ```python def load_tfrecord(tfrecord_dir, tfrecord_json=None): tfrecord_files = get_tfrecord_files(tfrecord_dir) # print("tfrecord files:\n{}".format("\n".join(tfrecord_files)), flush=True) data_schema = Schema() data_schema.add_column(name="input", de_type=mstype.int64, shape=[800]) data_schema.add_column(name="class", de_type=mstype.int64, shape=[1]) dataset = TFRecordDataset(dataset_files=tfrecord_files, schema=data_schema, shuffle=False) data_iter = dataset.create_dict_iterator() for item in data_iter: print(item, flush=True) break ``` 代码解读: - 这里使用了`Schema`对象,并且指定了列名,列的数据类型和数据维度。 保存并再次运行文件`load_tfrecord_dataset.py`,输出内容如下: > 可以看出能正确解析出之前保存在TFRecord内的数据,数据类型和数据维度解析正确。 ```shell {'input': Tensor(shape=[800], dtype=Int64, value= [1719, 636, 1063, 18, 742, 330, 385, 999, 837, 56, 529, 1000, ..... 135, 979, 1, 35, 166, 181, 90, 143]), 'class': Tensor(shape=[1], dtype=Int64, value= [0])} ``` #### 4.1.3 使用JSON文件 下面介绍,如何使用`JSON`文件来指定读取模型策略。 新建`tfrecord_sample.json`文件,在文件内写入如下内容: > numRows -- 数据列数 > > columns -- 依次为每列的列名、数据类型、数据维数、数据维度。 ```json { "datasetType": "TF", "numRows": 2, "columns": { "input": { "type": "int64", "rank": 1, "shape": [800] }, "class" : { "type": "int64", "rank": 1, "shape": [1] } } } ``` 有了相应的`JSON`文件,下面来介绍如何使用该文件进行数据读取。 修改`load_tfrecord`代码如下: ```python def load_tfrecord(tfrecord_dir, tfrecord_json=None): tfrecord_files = get_tfrecord_files(tfrecord_dir) # print("tfrecord files:\n{}".format("\n".join(tfrecord_files)), flush=True) dataset = TFRecordDataset(dataset_files=tfrecord_files, schema=tfrecord_json, shuffle=False) data_iter = dataset.create_dict_iterator() for item in data_iter: print(item, flush=True) break ``` 同时修改main部分代码如下: ```python load_tfrecord(tfrecord_dir=tfrecord_dir, tfrecord_json=tfrecord_json) ``` 代码解读 - 这里直接将`schema`参数指定为`JSON`的文件路径 保存并再次运行文件`load_tfrecord_dataset.py`,输出内容如下: ```shell {'class': Tensor(shape=[1], dtype=Int64, value= [0]), 'input': Tensor(shape=[800], dtype=Int64, value= [1719, 636, 1063, 18, ...... 135, 979, 1, 35, 166, 181, 90, 143])} ``` ### 4.2 columns_list使用 在某些场景下,我们可能只需要某(几)列的数据,而非全部数据,这时候就可以通过制定`columns_list`来进行数据加载。 下面我们只读取`class`列,来简单看看如何操作。 在`4.1.2`基础上,修改`load_tfrecord`代码如下: ```python def load_tfrecord(tfrecord_dir, tfrecord_json=None): tfrecord_files = get_tfrecord_files(tfrecord_dir) # print("tfrecord files:\n{}".format("\n".join(tfrecord_files)), flush=True) data_schema = Schema() data_schema.add_column(name="input", de_type=mstype.int64, shape=[800]) data_schema.add_column(name="class", de_type=mstype.int64, shape=[1]) dataset = TFRecordDataset(dataset_files=tfrecord_files, schema=data_schema, columns_list=["class"], shuffle=False) data_iter = dataset.create_dict_iterator() for item in data_iter: print(item, flush=True) break ``` 保存并再次运行文件`load_tfrecord_dataset.py`,输出内容如下: > 可以看到只读取了我们指定的列,且数据加载正确。 ```shell {'class': Tensor(shape=[1], dtype=Int64, value= [0])} ``` ## 5. 本文总结 本文介绍了在`MindSpore`中如何加载`TFRecord`数据集,并重点介绍了`TFRecordDataset`中的`schema`和`columns_list`参数使用。 ## 6. 本文参考 - [THUCTC: 一个高效的中文文本分类工具包](http://thuctc.thunlp.org/) - [THUCNews数据集](https://www.cxyzjd.com/article/qq_36047533/88094385) - [TFRecordDataset API](https://www.mindspore.cn/docs/zh-CN/r1.7/api_python/dataset/mindspore.dataset.TFRecordDataset.html#mindspore.dataset.TFRecordDataset) 本文为原创文章,版权归作者所有,未经授权不得转载!
  • [技术干货] python如何读取和存储dict()与.json格式文件【转载】
    目录读取和存储dict()与.json格式文件读取.json格式文件并将数据保存到字典中保存字典数据到.json文件中在命令行中输出字典时的乱码问题将字符串数据转化为字典数据将dict数据写入json文件中读取和存储dict()与.json格式文件读取.json格式文件并将数据保存到字典中数据文件:hg.json{"商家名称": "珍滋味港式粥火锅(工体店)", "评分": 27.0, "地址": "火锅工人体育场东路丙2号中国红街3号楼2层里", "人均消费": 174, "评论数量": 2307}{"商家名称": "井格老灶火锅(望京新世界店)", "评分": 26.2, "地址": "火锅望京广顺南大街路16号", "人均消费": 105, "评论数量": 1387}{"商家名称": "脸谱港式火锅(酒仙桥丽都店)", "评分": 24.5, "地址": "火锅芳园西路6号一层", "人均消费": 218, "评论数量": 39}针对上述数据,可以采用如下方法将json编码的字符串转换为python数据结构dict:12345678910# -*- coding: utf-8 -*-import jsonimport codecs data = []with codecs.open("hg.json", "r", "utf-8") as f:    for line in f:        dic = json.loads(line)        data.append(dic)        print(json.dumps(dic, indent=4, ensure_ascii=False, encoding='utf-8'))保存字典数据到.json文件中1234dic = {"商家名称": "井格老灶火锅(望京新世界店)", "评分": 26.2, "地址": "火锅望京广顺南大街路16号", "人均消费": 105, "评论数量": 1387}with codecs.open('hg.json','a', 'utf-8') as outf:    json.dump(dic, outf, ensure_ascii=False)    outf.write('\n')在命令行中输出字典时的乱码问题如果字典数据中有中文的话,print dic是无法正常显示中文的,可通过下面的方法格式化输出字典数据:12dic = {"北京": [446, 208.7, 110000], "天津": [454.2, 219.8, 120000], "上海": [498.6, 319.7, 310000]}print(json.dumps(dic, ensure_ascii=False, encoding='utf-8', indent=4))将字符串数据转化为字典数据两种转化方法123user = "{'name' : 'LiHua', 'sex' : 'male', 'age': 18}"dic1 = eval(user)exec("dic2="+user)补充一般来说,json解码时会从所提供的数据中创建出字典或者列表,如果想创建其它类型的对象,可以为json.loads()方法提供object_pairs_hook或者object_hook参数。下面的示例展示了我们应该如何将json数据解码为OrderedDict(有序字典),这样可以保持数据的顺序不变。123456>>> s = '{"name":"ACME", "SHARES":50, "PRICE":490}'>>> from collections import OrderedDict>>> data = json.load(s, object_pairs_hook=OrderedDict)>>> dataOrderedDict([('name', 'ACME'), ('shares', 50), ('price', 490)]>>>将dict数据写入json文件中现在获取一个医药网站的数据,最终转换成dict类型,需要将数据写入JSON文件中,以方便后面数据的使用12with open('./medical.json', 'w',encoding='utf-8') as fp:   json.dump(data, fp)但得到的最终数据却是这样:本来应该是正常的中文字符串,却是ASCII编码,因此在dump方法中添加一个ensure_ascii参数,原因是dump()方法将字典转化为字符串,会默认将其中unicode码以ascii编码的方式输入到字符串中12with open('./medical.json', 'w',encoding='utf-8') as fp:   json.dump(data, fp,ensure_ascii=False)
  • [其他问题] 【MindSpore Vision】【将分类模型部署到移动端】添加自定义模型时显示”文件不存在或json格式文件有误“
    【功能模块】将训练好的mobilenet模型部署到安卓手机,实现分类的功能。【操作步骤&问题现象】1、自主搭建模型,训练模型。2、将模型保存为MINDIR文件,通过MindSpore Lite conventer转换为.ms文件,传输到移动端。在电脑创建json文件(使用notepad++创建),并传输到移动端。3、下载安装MindSpore Vision apk4、手机打开MindSpore Vision应用,拍照后进入自定义界面,找到并选择json文件,显示”添加自定义模型成功“,title确实改变,但并未加载到ms文件,因为分类结果仍是通用模型的。【截图信息】【日志信息】(可选,上传日志内容或者附件)
  • [网络精度性能调优] 采集Profiling 数据(acl.json 配置文件方式 ),无法产生PROF***目录
    参考:https://www.hiascend.com/doc_center/source/zh/canncommercial/504/devtools/auxiliarydevtool/CANN%205.0.4%20%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7%E6%8C%87%E5%8D%97%2001.pdf采用配置acl.json的方式采集数据,如上述文档描述应有PROF*** 目录,但实际没有,却有JOB**************目录CANN 5.0.4DIRVER:21.0.4acl.json 如下:{"profiler":{"switch": "on","output": "output","aicpu": "on"}}
  • [干货汇总] 透过实例demo带你认识gRPC
    本文分享自华为云社区《[gRPC介绍以及spring demo构架展示](https://bbs.huaweicloud.com/blogs/342879?utm_source=csdn&utm_medium=bbs-ex&utm_campaign=paas&utm_content=content)》,作者:gentle_zhou。 gRPC,即google Remote Procedure Call Protocol;在gRPC里,客户端可以直接调用不同机器上的服务应用的方法,就像本地对象一样,所以创建分布式应用和服务就变简单了。 gRPC是基于定义一个服务,指定一个可以远程调用的带有参数和返回类型的的方法。在服务端,服务实现这个接口并且运行gRPC服务处理客户端调用。在客户端,有一个stub提供和服务端相同的方法。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/20/1653012226533359725.png) # 数据编码 数据编码即将请求的内存对象转化成可以传输的字节流发送给服务端,并将收到的字节流在转化成内存对象。 常见的数据编码方法有JSON,而gRPC则默认选用protobuf。 为什么选用protobuf呢?一个是因为它是谷歌自己的产品,二是它作为一种序列化资料结构的协定,在某些场景下传输的效率比JSON高。 一个.proto文件里的消息格式如下: ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/20/1653012247981492975.png) 而一个典型的JSON格式如下所示: ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/20/1653012255295334975.png) 我们可以看到在JSON里,内存方面,int字段的12345会占据5个字节,bool字段的true会占据4个字节,占据内存就会比较大,编码低效;还有一个缺点就是在JSON里,同一个接口同一个对象,只是int字段的值不同,每次却都还要传输int这个字段名。这样做的好处就是JSON的可读性很高,但同样在编码效率方面就会有所牺牲。 而Protobuf则是选用了VarInts对数字进行编码(VarInts则是动态的,征用了每个字节的最高位MSB,如果是1表示还有后序字节,如果是0表示后面就没字节了,以此来确定表示长度所需要的字节数量,解决了效率问题),同时给每个字段指定一个整数编号,传输的时候只传字段编号(解决了效率和冗余问题)。 但是只传字段编号的话,接收方如何知道各个编号对应哪个字段呢?那就需要靠提前约定了。Protobuf使用.proto文件当做密码本,记录字段和编号的对应关系。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/20/1653012264489474276.png) Protobuf 提供了一系列工具,为 proto 描述的 message 生成各种语言的代码。传输效率上去了,工具链也更加复杂了。 # 请求映射 IDL,Interactive Data Language的缩写,交互式数据语言。 因为我们有.proto文件作为IDL,Protobuf就可以做到RPC描述。比如在.proto文件里定义一个Greeter服务,其中有一个 SayHello 的方法,接受 HelloRequest 消息并返回 HelloReply 消息。如何实现这个 Greeter 则是语言无关的,所以叫 IDL。gRPC 就是用了 Protobuf 的 service 来描述 RPC 接口的。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/20/1653012289102692991.png) gRPC 在底层使用的是 HTTP/2 协议。这个 HTTP 请求用的是 POST 方法,对应的资源路径则是根据 .proto 定义确定的。我们前面提到的 Greeter 服务对应的路径是/demo.hello.Greeter/SayHello 。 一个 gRPC 定义包含三个部分,包名、服务名和接口名,连接规则如下 **/${包名}. ${服务名}/ ${接口名}** SayHello的包名是demo.hello,服务名是Greeter,接口名是SayHello,所以对应的路径就是 /demo.hello.Greeter/SayHello。 gRPC 支持三种流式接口,定义的办法就是在参数前加上 stream 关键字,分别是:请求流、响应流和双向流。 第一种叫请求流,可以在 RPC 发起之后不断发送新的请求消息。此类接口最典型的使用场景是发推送或者短信。 第二种叫响应流,可以在 RPC 发起之后不断接收新的响应消息。此类接口最典型的使用场景是订阅消息通知。 最后一种是双向流。可以在 RPC 发起之后同时收发消息。此类接口最典型的使用场景是实时语音转字幕。 如下就是普通接口和三种流式接口的结构样式: ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/20/1653012310100318117.png) 最简单的gRPC(非流式调用,unary)请求内容和相应内容如下所示: ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/20/1653012318083189876.png) 如果单看非流式调用,也就是 unary call,gRPC 并不复杂,跟普通的 HTTP 请求也没有太大区别。我们甚至可以使用 HTTP/1.1 来承载 gRPC 流量。但是gRPC 支持流式接口,这就有点难办了。 我们知道,HTTP/1.1 也是支持复用 TCP 连接的。但这种复用有一个明显的缺陷,所有请求必须排队。也就是说一定要按照请求、等待、响应、请求、等待、响应这样的顺序进行。先到先服务。而在实际的业务场景中肯定会有一些请求响应时间很长,客户端在收到响应之前会一直霸占着TCP连接。在这段时间里别的请求要么等待,要么发起新的 TCP 连接。在效率上确实有优化的余地。一言以蔽之,HTTP/1.1 不能充分地复用 TCP 连接。 后来,HTTP/2 横空出世!通过引入 stream 的概念,解决了 TCP 连接复用的问题。你可以把 HTTP/2 的 stream 简单理解为逻辑上的 TCP 连接,可以在一条 TCP 连接上并行收发 HTTP 消息,而无需像 HTTP/1.1 那样等待。 所以 gRPC 为了实现流式接品,选择使用 HTTP/2 进行通信。所以,前文的 Greeter 调用的实际通信内容长这个样子。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/20/1653012328545131463.png) HTTP/2 的 header 和 data 使用独立的 frame(帧,简单来说也是一种 Length-Prefixed 消息,是 HTTP/2 通信的基本单位) 发送,可以多次发送。 # springboot里的grpc demo 整个项目可以分成三个project: 1. grpc-springboot-demo-api:proto文件(syntax=“proto3”; 定义服务,定义请求体,定义回应内容)写好之后用maven-install来编译生成所需的类; 2. grpc-springboot-demo-server:pom文件(springboot的启动依赖,grpc的依赖, api项目的依赖),springboot的启动类,GRPC服务器的启动类,提供服务的业务逻辑实现类 3. grpc-springboot-demo-consumer:pom文件(springboot的启动依赖,grpc的依赖, api项目的依赖),springboot启动类(与服务端启动类无差异),gRPC 客户端(主要作用是监听 gRPC 服务端,开启通道)。 对应MVC关系就是: - grpc-springboot-demo-api就是service(接口,为提供实现); - grpc-springboot-demo-server就相当于serviceImpl(service的实现类); - grpc-springboot-demo-consumer就是controller的角色。 具体代码可以看:[grpc-java springboot 同步异步调用 demo_卤小蛋学编程的博客-CSDN博客_grpc java 异步](https://blog.csdn.net/Applying/article/details/115024675) 拓展 repeated限定修饰符 repeated代表可重复,我们可以理解为数组。 比如下面的代码: ``` syntax = "proto3";//指定版本信息,不指定会报错 message Person //message为关键字,作用为定义一种消息类型 { string name = 1; //姓名 int32 id = 2; //id string email = 3; //邮件 } message AddressBook { repeated Person people = 1; } ``` 编译器就会把Person认定为数组,而我们在使用Person,用add往里面添加信息,代码如下: AddressBook addBopookReq = AddressBook.newBuilder().addName("Lily").build(); 就不需要指定index了,直接往数组里添加了一个新的addressbook,它的名字属性则是Lily。 # 参考资料 1. https://developers.google.com/protocol-buffers/docs/overview 2. https://taoshu.in/grpc.html 3. https://grpc.io/ 4. https://grpc.io/docs/languages/java/quickstart/ 5. [protobuf入门教程(四):repeated限定修饰符_Mike江的博客-CSDN博客_protobuf的repeated](https://blog.csdn.net/tennysonsky/article/details/73921025)